@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.
- package/app/app.config.ts +239 -0
- package/app/app.vue +19 -0
- package/app/assets/css/tailwind.css +335 -0
- package/app/assets/css/theme.css +1171 -0
- package/app/components/OgImage/Docs.takumi.vue +69 -0
- package/app/components/OgImage/Landing.takumi.vue +64 -0
- package/app/components/OgImage/Site.takumi.vue +32 -0
- package/app/components/Ui/Avatar/Avatar.vue +75 -0
- package/app/components/Ui/Avatar/Badge.vue +34 -0
- package/app/components/Ui/Avatar/Fallback.vue +30 -0
- package/app/components/Ui/Avatar/Group.vue +29 -0
- package/app/components/Ui/Avatar/GroupCount.vue +29 -0
- package/app/components/Ui/Avatar/Image.vue +29 -0
- package/app/components/Ui/Badge.vue +86 -0
- package/app/components/Ui/BadgeGroup.vue +314 -0
- package/app/components/Ui/BgPattern/Grid.vue +137 -0
- package/app/components/Ui/BorderBeam.vue +97 -0
- package/app/components/Ui/Breadcrumbs.vue +103 -0
- package/app/components/Ui/Button.vue +169 -0
- package/app/components/Ui/ButtonGroup/ButtonGroup.vue +54 -0
- package/app/components/Ui/ButtonGroup/Separator.vue +38 -0
- package/app/components/Ui/ButtonGroup/Text.vue +33 -0
- package/app/components/Ui/Card/Action.vue +31 -0
- package/app/components/Ui/Card/Card.vue +58 -0
- package/app/components/Ui/Card/Content.vue +34 -0
- package/app/components/Ui/Card/Description.vue +36 -0
- package/app/components/Ui/Card/Footer.vue +31 -0
- package/app/components/Ui/Card/Header.vue +31 -0
- package/app/components/Ui/Card/Title.vue +37 -0
- package/app/components/Ui/Collapsible/Collapsible.vue +16 -0
- package/app/components/Ui/Collapsible/Content.vue +27 -0
- package/app/components/Ui/Collapsible/Trigger.vue +13 -0
- package/app/components/Ui/Command/Command.vue +115 -0
- package/app/components/Ui/Command/Dialog.vue +65 -0
- package/app/components/Ui/Command/Empty.vue +32 -0
- package/app/components/Ui/Command/Footer.vue +40 -0
- package/app/components/Ui/Command/Group.vue +58 -0
- package/app/components/Ui/Command/Input.vue +57 -0
- package/app/components/Ui/Command/Item.vue +102 -0
- package/app/components/Ui/Command/List.vue +33 -0
- package/app/components/Ui/Command/Separator.vue +30 -0
- package/app/components/Ui/Command/Shortcut.vue +28 -0
- package/app/components/Ui/Container.vue +46 -0
- package/app/components/Ui/ContextMenu/Arrow.vue +43 -0
- package/app/components/Ui/ContextMenu/CheckboxItem.vue +42 -0
- package/app/components/Ui/ContextMenu/Content.vue +53 -0
- package/app/components/Ui/ContextMenu/ContextMenu.vue +14 -0
- package/app/components/Ui/ContextMenu/Group.vue +12 -0
- package/app/components/Ui/ContextMenu/Item.vue +65 -0
- package/app/components/Ui/ContextMenu/ItemIndicator.vue +30 -0
- package/app/components/Ui/ContextMenu/Label.vue +35 -0
- package/app/components/Ui/ContextMenu/Portal.vue +12 -0
- package/app/components/Ui/ContextMenu/RadioGroup.vue +16 -0
- package/app/components/Ui/ContextMenu/RadioItem.vue +40 -0
- package/app/components/Ui/ContextMenu/Separator.vue +25 -0
- package/app/components/Ui/ContextMenu/Shortcut.vue +30 -0
- package/app/components/Ui/ContextMenu/Sub.vue +15 -0
- package/app/components/Ui/ContextMenu/SubContent.vue +55 -0
- package/app/components/Ui/ContextMenu/SubTrigger.vue +42 -0
- package/app/components/Ui/ContextMenu/Trigger.vue +12 -0
- package/app/components/Ui/Dialog/Close.vue +12 -0
- package/app/components/Ui/Dialog/Content.vue +79 -0
- package/app/components/Ui/Dialog/Description.vue +29 -0
- package/app/components/Ui/Dialog/Dialog.vue +14 -0
- package/app/components/Ui/Dialog/Footer.vue +32 -0
- package/app/components/Ui/Dialog/Header.vue +32 -0
- package/app/components/Ui/Dialog/Overlay.vue +25 -0
- package/app/components/Ui/Dialog/Portal.vue +12 -0
- package/app/components/Ui/Dialog/Title.vue +29 -0
- package/app/components/Ui/Dialog/Trigger.vue +12 -0
- package/app/components/Ui/Divider.vue +102 -0
- package/app/components/Ui/Drawer/Close.vue +12 -0
- package/app/components/Ui/Drawer/Content.vue +67 -0
- package/app/components/Ui/Drawer/Description.vue +33 -0
- package/app/components/Ui/Drawer/Drawer.vue +17 -0
- package/app/components/Ui/Drawer/Footer.vue +35 -0
- package/app/components/Ui/Drawer/Header.vue +34 -0
- package/app/components/Ui/Drawer/Overlay.vue +20 -0
- package/app/components/Ui/Drawer/Portal.vue +12 -0
- package/app/components/Ui/Drawer/Title.vue +35 -0
- package/app/components/Ui/Drawer/Trigger.vue +12 -0
- package/app/components/Ui/DropdownMenu/Arrow.vue +43 -0
- package/app/components/Ui/DropdownMenu/CheckboxItem.vue +41 -0
- package/app/components/Ui/DropdownMenu/Content.vue +56 -0
- package/app/components/Ui/DropdownMenu/DropdownMenu.vue +15 -0
- package/app/components/Ui/DropdownMenu/Group.vue +12 -0
- package/app/components/Ui/DropdownMenu/Item.vue +64 -0
- package/app/components/Ui/DropdownMenu/ItemIndicator.vue +32 -0
- package/app/components/Ui/DropdownMenu/Label.vue +34 -0
- package/app/components/Ui/DropdownMenu/Portal.vue +12 -0
- package/app/components/Ui/DropdownMenu/RadioGroup.vue +16 -0
- package/app/components/Ui/DropdownMenu/RadioItem.vue +40 -0
- package/app/components/Ui/DropdownMenu/Separator.vue +25 -0
- package/app/components/Ui/DropdownMenu/Shortcut.vue +44 -0
- package/app/components/Ui/DropdownMenu/Sub.vue +14 -0
- package/app/components/Ui/DropdownMenu/SubContent.vue +56 -0
- package/app/components/Ui/DropdownMenu/SubTrigger.vue +47 -0
- package/app/components/Ui/DropdownMenu/Trigger.vue +12 -0
- package/app/components/Ui/FancyIcon.vue +243 -0
- package/app/components/Ui/GradientDivider.vue +26 -0
- package/app/components/Ui/IframeLazy.vue +97 -0
- package/app/components/Ui/Input.vue +97 -0
- package/app/components/Ui/InputGroup/Addon.vue +66 -0
- package/app/components/Ui/InputGroup/Button.vue +50 -0
- package/app/components/Ui/InputGroup/Input.vue +25 -0
- package/app/components/Ui/InputGroup/InputGroup.vue +44 -0
- package/app/components/Ui/InputGroup/Text.vue +33 -0
- package/app/components/Ui/InputGroup/Textarea.vue +25 -0
- package/app/components/Ui/Kbd/Group.vue +32 -0
- package/app/components/Ui/Kbd/Kbd.vue +62 -0
- package/app/components/Ui/Label.vue +40 -0
- package/app/components/Ui/Popover/Anchor.vue +13 -0
- package/app/components/Ui/Popover/Arrow.vue +42 -0
- package/app/components/Ui/Popover/Close.vue +13 -0
- package/app/components/Ui/Popover/Content.vue +55 -0
- package/app/components/Ui/Popover/Popover.vue +15 -0
- package/app/components/Ui/Popover/Portal.vue +13 -0
- package/app/components/Ui/Popover/Trigger.vue +12 -0
- package/app/components/Ui/Popover/X.vue +38 -0
- package/app/components/Ui/ScrollArea/Corner.vue +12 -0
- package/app/components/Ui/ScrollArea/ScrollArea.vue +38 -0
- package/app/components/Ui/ScrollArea/Scrollbar.vue +39 -0
- package/app/components/Ui/ScrollArea/Thumb.vue +39 -0
- package/app/components/Ui/ScrollArea/Viewport.vue +27 -0
- package/app/components/Ui/Scrollspy.vue +61 -0
- package/app/components/Ui/Separator.vue +40 -0
- package/app/components/Ui/Skeleton.vue +39 -0
- package/app/components/Ui/Sonner.vue +25 -0
- package/app/components/Ui/Tabs/Content.vue +27 -0
- package/app/components/Ui/Tabs/Indicator.vue +37 -0
- package/app/components/Ui/Tabs/List.vue +37 -0
- package/app/components/Ui/Tabs/Tabs.vue +25 -0
- package/app/components/Ui/Tabs/Trigger.vue +41 -0
- package/app/components/Ui/Textarea.vue +81 -0
- package/app/components/Ui/Tooltip/Arrow.vue +30 -0
- package/app/components/Ui/Tooltip/Content.vue +50 -0
- package/app/components/Ui/Tooltip/Portal.vue +12 -0
- package/app/components/Ui/Tooltip/Provider.vue +14 -0
- package/app/components/Ui/Tooltip/Tooltip.vue +25 -0
- package/app/components/Ui/Tooltip/Trigger.vue +12 -0
- package/app/components/Ui/Tree/Item.vue +15 -0
- package/app/components/Ui/Tree/Tree.vue +15 -0
- package/app/components/Ui/Tree/Virtualizer.vue +13 -0
- package/app/components/component-api/ComponentApiSection.vue +135 -0
- package/app/components/content/docs/Callout/DocsCallout.vue +99 -0
- package/app/components/content/prose/ProseA.global.vue +31 -0
- package/app/components/content/prose/ProseBlockquote.global.vue +28 -0
- package/app/components/content/prose/ProseCallout.global.vue +306 -0
- package/app/components/content/prose/ProseCard.global.vue +100 -0
- package/app/components/content/prose/ProseCodeCollapse.global.vue +125 -0
- package/app/components/content/prose/ProseCodeCopy.global.vue +67 -0
- package/app/components/content/prose/ProseCodeGroup.global.vue +104 -0
- package/app/components/content/prose/ProseCodeTree.global.vue +277 -0
- package/app/components/content/prose/ProseCollapsible.global.vue +64 -0
- package/app/components/content/prose/ProseColorModeImage.global.vue +58 -0
- package/app/components/content/prose/ProseComponentApi.global.vue +104 -0
- package/app/components/content/prose/ProseComponentEvents.global.vue +27 -0
- package/app/components/content/prose/ProseComponentExposed.global.vue +27 -0
- package/app/components/content/prose/ProseComponentProps.global.vue +27 -0
- package/app/components/content/prose/ProseComponentSlots.global.vue +27 -0
- package/app/components/content/prose/ProseEm.global.vue +5 -0
- package/app/components/content/prose/ProseField.global.vue +118 -0
- package/app/components/content/prose/ProseFieldGroup.global.vue +62 -0
- package/app/components/content/prose/ProseH1.global.vue +58 -0
- package/app/components/content/prose/ProseH2.global.vue +58 -0
- package/app/components/content/prose/ProseH3.global.vue +58 -0
- package/app/components/content/prose/ProseH4.global.vue +58 -0
- package/app/components/content/prose/ProseH5.global.vue +47 -0
- package/app/components/content/prose/ProseH6.global.vue +47 -0
- package/app/components/content/prose/ProseHr.global.vue +33 -0
- package/app/components/content/prose/ProseIconList.global.vue +113 -0
- package/app/components/content/prose/ProseImg.global.vue +119 -0
- package/app/components/content/prose/ProseLead.global.vue +28 -0
- package/app/components/content/prose/ProseLi.global.vue +25 -0
- package/app/components/content/prose/ProseMermaid.global.vue +127 -0
- package/app/components/content/prose/ProseOl.global.vue +25 -0
- package/app/components/content/prose/ProseP.global.vue +25 -0
- package/app/components/content/prose/ProsePmInstall.global.vue +60 -0
- package/app/components/content/prose/ProsePmRun.global.vue +51 -0
- package/app/components/content/prose/ProsePmX.global.vue +56 -0
- package/app/components/content/prose/ProsePre.global.vue +181 -0
- package/app/components/content/prose/ProseShowCase.global.vue +56 -0
- package/app/components/content/prose/ProseSmartIcon.global.vue +54 -0
- package/app/components/content/prose/ProseStep.global.vue +22 -0
- package/app/components/content/prose/ProseSteps.global.vue +22 -0
- package/app/components/content/prose/ProseStrong.global.vue +34 -0
- package/app/components/content/prose/ProseTable.global.vue +34 -0
- package/app/components/content/prose/ProseTabs.global.vue +104 -0
- package/app/components/content/prose/ProseTabsInner.global.vue +274 -0
- package/app/components/content/prose/ProseTbody.global.vue +5 -0
- package/app/components/content/prose/ProseTd.global.vue +25 -0
- package/app/components/content/prose/ProseTh.global.vue +25 -0
- package/app/components/content/prose/ProseThead.global.vue +9 -0
- package/app/components/content/prose/ProseTr.global.vue +35 -0
- package/app/components/content/prose/ProseUl.global.vue +25 -0
- package/app/components/docs/DocsExtraStuff.vue +39 -0
- package/app/components/docs/DocsGithubLink.vue +11 -0
- package/app/components/docs/DocsHeader.vue +3 -0
- package/app/components/docs/DocsLogo.vue +74 -0
- package/app/components/docs/DocsMobileBar.vue +26 -0
- package/app/components/docs/DocsMobileNav.vue +49 -0
- package/app/components/docs/DocsNav.vue +161 -0
- package/app/components/docs/DocsPageTransition.vue +32 -0
- package/app/components/docs/DocsSearch.vue +220 -0
- package/app/components/docs/DocsSearchButton.vue +20 -0
- package/app/components/docs/DocsThemeCustomizer.vue +229 -0
- package/app/components/docs/DocsThemeToggler.vue +98 -0
- package/app/components/docs/DocsTocRail.vue +162 -0
- package/app/components/docs/DocsToclink.vue +52 -0
- package/app/components/docs/DocsZigZagRail.vue +70 -0
- package/app/components/landing/Hero.vue +222 -0
- package/app/components/page/PageFooter.vue +49 -0
- package/app/components/page/PageHeader.vue +68 -0
- package/app/components/page/PageHeaderLinks.vue +138 -0
- package/app/components/page/PageMobileToc.vue +162 -0
- package/app/components/page/PageToc.vue +33 -0
- package/app/composables/useDefaultSlotItems.ts +90 -0
- package/app/composables/useDocPage.ts +66 -0
- package/app/composables/useDocSearch.ts +23 -0
- package/app/composables/useDocd.ts +107 -0
- package/app/composables/useLogoAssets.ts +319 -0
- package/app/composables/usePm.ts +73 -0
- package/app/composables/useProseComponentMeta.ts +30 -0
- package/app/composables/useScrollspy.ts +327 -0
- package/app/composables/useSeo.ts +240 -0
- package/app/composables/useUIConfig.ts +36 -0
- package/app/layouts/default.vue +5 -0
- package/app/layouts/docs.vue +64 -0
- package/app/middleware/colorMode.global.ts +8 -0
- package/app/middleware/doc-page-layout.ts +11 -0
- package/app/middleware/landing-page-layout.ts +11 -0
- package/app/pages/[...slug].vue +63 -0
- package/app/plugins/mermaid.client.ts +35 -0
- package/app/plugins/navigation-direction.client.ts +15 -0
- package/app/plugins/theme-restore.client.ts +7 -0
- package/app/templates/landing.vue +40 -0
- package/app/transitions/presets.ts +71 -0
- package/app/types/prose-component-meta.d.ts +4 -0
- package/app/utils/og.ts +26 -0
- package/app/utils/prerender.ts +9 -0
- package/app/utils/translucent.ts +56 -0
- package/app/utils/tw-helper.ts +4 -0
- package/content.config.ts +81 -0
- package/index.d.ts +25 -0
- package/modules/config.ts +89 -0
- package/modules/custom-icons.ts +17 -0
- package/modules/markdown-rewrite.ts +96 -0
- package/modules/prose-component-meta.ts +150 -0
- package/modules/routing.ts +22 -0
- package/modules/skills/index.ts +167 -0
- package/modules/skills/runtime/server/routes/skills-files.ts +49 -0
- package/modules/skills/runtime/server/routes/skills-index.ts +8 -0
- package/nuxt.config.ts +96 -0
- package/nuxt.schema.ts +288 -0
- package/package.json +95 -0
- package/server/mcp/tools/get-page.ts +66 -0
- package/server/mcp/tools/list-pages.ts +61 -0
- package/server/routes/sitemap.xml.ts +73 -0
- package/server/utils/content.ts +3 -0
- package/tailwind.config.cjs +55 -0
- package/utils/component-api-content.ts +467 -0
- package/utils/component-api-discovery.ts +165 -0
- package/utils/component-api-schema.ts +28 -0
- package/utils/component-api.ts +243 -0
- package/utils/generate-prose-component-meta.ts +303 -0
- package/utils/git.ts +123 -0
- package/utils/meta.ts +40 -0
- 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
|
+
}
|