@heyo-sh/create-heyo-docs 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/deployments/cloudflare/astro/package.json +12 -0
- package/deployments/cloudflare/astro/platform.astro.ts +4 -0
- package/deployments/cloudflare/astro/wrangler.jsonc +6 -0
- package/deployments/cloudflare/next/open-next.config.ts +3 -0
- package/deployments/cloudflare/next/package.json +13 -0
- package/deployments/cloudflare/next/wrangler.jsonc +11 -0
- package/deployments/cloudflare/react-router/app/entry.server.tsx +48 -0
- package/deployments/cloudflare/react-router/package.json +21 -0
- package/deployments/cloudflare/react-router/platform.vite.ts +6 -0
- package/deployments/cloudflare/react-router/react-router.config.ts +20 -0
- package/deployments/cloudflare/react-router/workers/app.ts +12 -0
- package/deployments/cloudflare/react-router/wrangler.jsonc +10 -0
- package/deployments/vercel/astro/package.json +11 -0
- package/deployments/vercel/astro/platform.astro.ts +4 -0
- package/deployments/vercel/astro/vercel.json +4 -0
- package/deployments/vercel/next/package.json +8 -0
- package/deployments/vercel/next/vercel.json +3 -0
- package/deployments/vercel/react-router/package.json +12 -0
- package/deployments/vercel/react-router/platform.vite.ts +4 -0
- package/deployments/vercel/react-router/react-router.config.ts +22 -0
- package/deployments/vercel/react-router/vercel.json +4 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +328 -0
- package/dist/package-manager.d.ts +6 -0
- package/dist/package-manager.d.ts.map +1 -0
- package/dist/project-name.d.ts +3 -0
- package/dist/project-name.d.ts.map +1 -0
- package/dist/scaffold.d.ts +5 -0
- package/dist/scaffold.d.ts.map +1 -0
- package/dist/types.d.ts +19 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/utils.d.ts +8 -0
- package/dist/utils.d.ts.map +1 -0
- package/package.json +36 -0
- package/templates/astro/astro.config.ts +15 -0
- package/templates/astro/components.json +25 -0
- package/templates/astro/content/getting-started.mdx +16 -0
- package/templates/astro/content/index.mdx +22 -0
- package/templates/astro/content/mdx-components.mdx +44 -0
- package/templates/astro/heyo-docs.config.ts +24 -0
- package/templates/astro/package.json +34 -0
- package/templates/astro/platform.astro.ts +4 -0
- package/templates/astro/public/apple-touch-icon.png +0 -0
- package/templates/astro/public/favicon-96x96.png +0 -0
- package/templates/astro/public/favicon.ico +0 -0
- package/templates/astro/public/favicon.svg +1 -0
- package/templates/astro/public/logo.svg +12 -0
- package/templates/astro/public/site.webmanifest +21 -0
- package/templates/astro/public/web-app-manifest-192x192.png +0 -0
- package/templates/astro/public/web-app-manifest-512x512.png +0 -0
- package/templates/astro/src/components/docs-app.tsx +165 -0
- package/templates/astro/src/components/theme-provider.tsx +106 -0
- package/templates/astro/src/env.d.ts +2 -0
- package/templates/astro/src/heyo-docs-icons.tsx +79 -0
- package/templates/astro/src/layouts/docs-layout.astro +70 -0
- package/templates/astro/src/lib/docs.ts +31 -0
- package/templates/astro/src/lib/openapi-request.ts +160 -0
- package/templates/astro/src/lib/seo.ts +221 -0
- package/templates/astro/src/lib/theme.ts +74 -0
- package/templates/astro/src/pages/[...slug].astro +50 -0
- package/templates/astro/src/pages/[...slug].md.ts +28 -0
- package/templates/astro/src/pages/heyo-docs-internal/openapi-request.ts +8 -0
- package/templates/astro/src/pages/llms-full.txt.ts +13 -0
- package/templates/astro/src/pages/llms.txt.ts +13 -0
- package/templates/astro/src/pages/robots.txt.ts +19 -0
- package/templates/astro/src/pages/rss.xml.ts +13 -0
- package/templates/astro/src/pages/sitemap.xml.ts +19 -0
- package/templates/astro/src/styles/app.css +8 -0
- package/templates/astro/src/virtual.d.ts +19 -0
- package/templates/astro/tsconfig.json +12 -0
- package/templates/next/app/[[...slug]]/page.tsx +59 -0
- package/templates/next/app/app.css +8 -0
- package/templates/next/app/components/docs-app.tsx +139 -0
- package/templates/next/app/components/theme-provider.tsx +93 -0
- package/templates/next/app/error.tsx +8 -0
- package/templates/next/app/heyo-docs-icons.tsx +70 -0
- package/templates/next/app/heyo-docs-internal/markdown/[[...slug]]/route.ts +26 -0
- package/templates/next/app/heyo-docs-internal/openapi-request/route.ts +5 -0
- package/templates/next/app/layout.tsx +79 -0
- package/templates/next/app/lib/docs.ts +43 -0
- package/templates/next/app/lib/openapi-request.ts +147 -0
- package/templates/next/app/lib/seo.ts +189 -0
- package/templates/next/app/lib/theme.ts +61 -0
- package/templates/next/app/llms-full.txt/route.ts +10 -0
- package/templates/next/app/llms.txt/route.ts +10 -0
- package/templates/next/app/not-found.tsx +13 -0
- package/templates/next/app/robots.txt/route.ts +16 -0
- package/templates/next/app/rss.xml/route.ts +10 -0
- package/templates/next/app/sitemap.xml/route.ts +14 -0
- package/templates/next/components.json +20 -0
- package/templates/next/content/getting-started.mdx +16 -0
- package/templates/next/content/index.mdx +22 -0
- package/templates/next/content/mdx-components.mdx +41 -0
- package/templates/next/heyo-docs.config.ts +24 -0
- package/templates/next/mdx.d.ts +6 -0
- package/templates/next/next-env.d.ts +7 -0
- package/templates/next/next.config.ts +23 -0
- package/templates/next/package.json +40 -0
- package/templates/next/postcss.config.mjs +5 -0
- package/templates/next/public/apple-touch-icon.png +0 -0
- package/templates/next/public/favicon-96x96.png +0 -0
- package/templates/next/public/favicon.ico +0 -0
- package/templates/next/public/favicon.svg +1 -0
- package/templates/next/public/logo.svg +12 -0
- package/templates/next/public/site.webmanifest +21 -0
- package/templates/next/public/web-app-manifest-192x192.png +0 -0
- package/templates/next/public/web-app-manifest-512x512.png +0 -0
- package/templates/next/scripts/generate-heyo-docs.ts +50 -0
- package/templates/next/tsconfig.json +27 -0
- package/templates/react-router/app/app.css +8 -0
- package/templates/react-router/app/components/theme-provider.tsx +106 -0
- package/templates/react-router/app/heyo-docs-icons.tsx +71 -0
- package/templates/react-router/app/lib/theme.ts +74 -0
- package/templates/react-router/app/root.tsx +140 -0
- package/templates/react-router/app/routes/docs.tsx +179 -0
- package/templates/react-router/app/routes/home.tsx +1 -0
- package/templates/react-router/app/routes/llms-full.ts +13 -0
- package/templates/react-router/app/routes/llms.ts +13 -0
- package/templates/react-router/app/routes/markdown.ts +25 -0
- package/templates/react-router/app/routes/openapi-request.ts +159 -0
- package/templates/react-router/app/routes/page.tsx +1 -0
- package/templates/react-router/app/routes/robots.ts +19 -0
- package/templates/react-router/app/routes/rss.ts +13 -0
- package/templates/react-router/app/routes/sitemap.ts +18 -0
- package/templates/react-router/app/routes.ts +13 -0
- package/templates/react-router/app/virtual.d.ts +19 -0
- package/templates/react-router/components.json +25 -0
- package/templates/react-router/content/getting-started.mdx +16 -0
- package/templates/react-router/content/index.mdx +22 -0
- package/templates/react-router/content/mdx-components.mdx +293 -0
- package/templates/react-router/heyo-docs.config.ts +24 -0
- package/templates/react-router/package.json +36 -0
- package/templates/react-router/platform.vite.ts +2 -0
- package/templates/react-router/public/apple-touch-icon.png +0 -0
- package/templates/react-router/public/favicon-96x96.png +0 -0
- package/templates/react-router/public/favicon.ico +0 -0
- package/templates/react-router/public/favicon.svg +1 -0
- package/templates/react-router/public/logo.svg +12 -0
- package/templates/react-router/public/site.webmanifest +21 -0
- package/templates/react-router/public/web-app-manifest-192x192.png +0 -0
- package/templates/react-router/public/web-app-manifest-512x512.png +0 -0
- package/templates/react-router/react-router.config.ts +20 -0
- package/templates/react-router/tsconfig.json +20 -0
- package/templates/react-router/vite.config.ts +17 -0
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import {
|
|
2
|
+
navigationGroupContainsPath,
|
|
3
|
+
navigationPages,
|
|
4
|
+
navigationSectionPathForPath,
|
|
5
|
+
type ChangelogGroupConfig,
|
|
6
|
+
type DocsPage,
|
|
7
|
+
type HeyoDocsConfig,
|
|
8
|
+
type NavigationGroup,
|
|
9
|
+
type OpenApiEndpoint,
|
|
10
|
+
} from "@heyo-sh/heyo-docs";
|
|
11
|
+
|
|
12
|
+
interface DocsSeoInput {
|
|
13
|
+
config: HeyoDocsConfig;
|
|
14
|
+
pathname: string;
|
|
15
|
+
page?: DocsPage;
|
|
16
|
+
endpoint?: OpenApiEndpoint;
|
|
17
|
+
changelogGroup?: ChangelogGroupConfig;
|
|
18
|
+
navigation?: NavigationGroup[];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function canonicalFor(
|
|
22
|
+
config: HeyoDocsConfig,
|
|
23
|
+
pathname: string,
|
|
24
|
+
page?: DocsPage,
|
|
25
|
+
): string | undefined {
|
|
26
|
+
return (
|
|
27
|
+
page?.seo.canonical ??
|
|
28
|
+
(config.siteUrl ? `${config.siteUrl}${pathname}` : undefined)
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function website(config: HeyoDocsConfig) {
|
|
33
|
+
return {
|
|
34
|
+
"@type": "WebSite",
|
|
35
|
+
name: config.title,
|
|
36
|
+
...(config.siteUrl ? { url: config.siteUrl } : {}),
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
interface BreadcrumbItem {
|
|
41
|
+
label: string;
|
|
42
|
+
href?: string;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function breadcrumbItems(
|
|
46
|
+
navigation: NavigationGroup[] | undefined,
|
|
47
|
+
pathname: string,
|
|
48
|
+
title: string,
|
|
49
|
+
changelogGroup: ChangelogGroupConfig | undefined,
|
|
50
|
+
): BreadcrumbItem[] {
|
|
51
|
+
if (changelogGroup) return [{ label: changelogGroup.group, href: pathname }];
|
|
52
|
+
|
|
53
|
+
const group = navigation?.find((candidate) =>
|
|
54
|
+
navigationGroupContainsPath(candidate, pathname),
|
|
55
|
+
);
|
|
56
|
+
const sections = group
|
|
57
|
+
? navigationSectionPathForPath(group.sections, pathname)
|
|
58
|
+
: undefined;
|
|
59
|
+
|
|
60
|
+
if (!group) return [{ label: title, href: pathname }];
|
|
61
|
+
|
|
62
|
+
const groupHref = navigationPages(group.sections)[0]?.slug;
|
|
63
|
+
return [
|
|
64
|
+
{ label: group.group, href: groupHref },
|
|
65
|
+
...(sections ?? []).map((section) => ({
|
|
66
|
+
label: section.section,
|
|
67
|
+
href: navigationPages([section])[0]?.slug,
|
|
68
|
+
})),
|
|
69
|
+
{ label: title, href: pathname },
|
|
70
|
+
];
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function absoluteUrl(siteUrl: string | undefined, href: string | undefined) {
|
|
74
|
+
if (!siteUrl || !href) return undefined;
|
|
75
|
+
if (/^https?:\/\//.test(href)) return href;
|
|
76
|
+
return new URL(href, `${siteUrl}/`).toString();
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function breadcrumbs(
|
|
80
|
+
config: HeyoDocsConfig,
|
|
81
|
+
canonical: string | undefined,
|
|
82
|
+
title: string,
|
|
83
|
+
navigation: NavigationGroup[] | undefined,
|
|
84
|
+
pathname: string,
|
|
85
|
+
changelogGroup: ChangelogGroupConfig | undefined,
|
|
86
|
+
) {
|
|
87
|
+
const items = breadcrumbItems(navigation, pathname, title, changelogGroup);
|
|
88
|
+
|
|
89
|
+
return {
|
|
90
|
+
"@context": "https://schema.org",
|
|
91
|
+
"@type": "BreadcrumbList",
|
|
92
|
+
itemListElement: [
|
|
93
|
+
{ label: config.title, href: config.siteUrl },
|
|
94
|
+
...items,
|
|
95
|
+
].map((item, index, allItems) => {
|
|
96
|
+
const itemUrl =
|
|
97
|
+
index === allItems.length - 1
|
|
98
|
+
? canonical
|
|
99
|
+
: absoluteUrl(config.siteUrl, item.href);
|
|
100
|
+
return {
|
|
101
|
+
"@type": "ListItem",
|
|
102
|
+
position: index + 1,
|
|
103
|
+
name: item.label,
|
|
104
|
+
...(itemUrl ? { item: itemUrl } : {}),
|
|
105
|
+
};
|
|
106
|
+
}),
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function endpointUrlTemplate(endpoint: OpenApiEndpoint) {
|
|
111
|
+
const server = endpoint.servers[0]?.replace(/\/$/, "");
|
|
112
|
+
return server ? `${server}${endpoint.path}` : endpoint.path;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function structuredData({
|
|
116
|
+
changelogGroup,
|
|
117
|
+
config,
|
|
118
|
+
endpoint,
|
|
119
|
+
page,
|
|
120
|
+
canonical,
|
|
121
|
+
description,
|
|
122
|
+
title,
|
|
123
|
+
}: Omit<DocsSeoInput, "pathname"> & {
|
|
124
|
+
canonical?: string;
|
|
125
|
+
description: string;
|
|
126
|
+
title: string;
|
|
127
|
+
}) {
|
|
128
|
+
if (endpoint) {
|
|
129
|
+
const entryPoint = {
|
|
130
|
+
"@type": "EntryPoint",
|
|
131
|
+
name:
|
|
132
|
+
endpoint.operationId ??
|
|
133
|
+
`${endpoint.method.toUpperCase()} ${endpoint.path}`,
|
|
134
|
+
httpMethod: endpoint.method.toUpperCase(),
|
|
135
|
+
urlTemplate: endpointUrlTemplate(endpoint),
|
|
136
|
+
...(endpoint.requestBody?.contentType
|
|
137
|
+
? { encodingType: endpoint.requestBody.contentType }
|
|
138
|
+
: {}),
|
|
139
|
+
...(endpoint.responses[0]?.contentType
|
|
140
|
+
? { contentType: endpoint.responses[0].contentType }
|
|
141
|
+
: {}),
|
|
142
|
+
};
|
|
143
|
+
return {
|
|
144
|
+
"@context": "https://schema.org",
|
|
145
|
+
"@type": "APIReference",
|
|
146
|
+
headline: title,
|
|
147
|
+
description,
|
|
148
|
+
...(canonical ? { url: canonical } : {}),
|
|
149
|
+
isPartOf: website(config),
|
|
150
|
+
mainEntity: entryPoint,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (page && changelogGroup) {
|
|
155
|
+
return {
|
|
156
|
+
"@context": "https://schema.org",
|
|
157
|
+
"@type": "CollectionPage",
|
|
158
|
+
headline: title,
|
|
159
|
+
description,
|
|
160
|
+
...(canonical ? { url: canonical } : {}),
|
|
161
|
+
isPartOf: website(config),
|
|
162
|
+
...(page.changelogUpdates?.length
|
|
163
|
+
? {
|
|
164
|
+
hasPart: page.changelogUpdates.map((update) => ({
|
|
165
|
+
"@type": "TechArticle",
|
|
166
|
+
headline: update.label,
|
|
167
|
+
...(update.tags.length
|
|
168
|
+
? { keywords: update.tags.join(", ") }
|
|
169
|
+
: {}),
|
|
170
|
+
...(canonical ? { url: `${canonical}#${update.id}` } : {}),
|
|
171
|
+
})),
|
|
172
|
+
}
|
|
173
|
+
: {}),
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return {
|
|
178
|
+
"@context": "https://schema.org",
|
|
179
|
+
"@type": "TechArticle",
|
|
180
|
+
headline: title,
|
|
181
|
+
description,
|
|
182
|
+
...(canonical ? { url: canonical } : {}),
|
|
183
|
+
isPartOf: website(config),
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
export function docsSeo(input: DocsSeoInput) {
|
|
188
|
+
const { changelogGroup, config, endpoint, navigation, page, pathname } =
|
|
189
|
+
input;
|
|
190
|
+
const title = page?.seo.title ?? `${endpoint!.title} | ${config.title}`;
|
|
191
|
+
const description =
|
|
192
|
+
page?.seo.description ??
|
|
193
|
+
endpoint?.description ??
|
|
194
|
+
`${endpoint!.method.toUpperCase()} ${endpoint!.path} API endpoint.`;
|
|
195
|
+
const canonical = canonicalFor(config, pathname, page);
|
|
196
|
+
|
|
197
|
+
return {
|
|
198
|
+
title,
|
|
199
|
+
description,
|
|
200
|
+
canonical,
|
|
201
|
+
structuredData: [
|
|
202
|
+
structuredData({
|
|
203
|
+
changelogGroup,
|
|
204
|
+
config,
|
|
205
|
+
endpoint,
|
|
206
|
+
page,
|
|
207
|
+
canonical,
|
|
208
|
+
description,
|
|
209
|
+
title,
|
|
210
|
+
}),
|
|
211
|
+
breadcrumbs(
|
|
212
|
+
config,
|
|
213
|
+
canonical,
|
|
214
|
+
page?.title ?? endpoint!.title,
|
|
215
|
+
navigation,
|
|
216
|
+
pathname,
|
|
217
|
+
changelogGroup,
|
|
218
|
+
),
|
|
219
|
+
],
|
|
220
|
+
};
|
|
221
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
export type Theme = "dark" | "light" | "system";
|
|
2
|
+
export type ResolvedTheme = Exclude<Theme, "system">;
|
|
3
|
+
|
|
4
|
+
export const DEFAULT_THEME: Theme = "system";
|
|
5
|
+
export const THEME_STORAGE_KEY = "heyo-docs-theme";
|
|
6
|
+
|
|
7
|
+
const themeValues = new Set<Theme>(["light", "dark", "system"]);
|
|
8
|
+
|
|
9
|
+
export function isTheme(value: string | null): value is Theme {
|
|
10
|
+
return value !== null && themeValues.has(value as Theme);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function getStoredTheme(
|
|
14
|
+
storageKey: string,
|
|
15
|
+
fallbackTheme: Theme,
|
|
16
|
+
): Theme {
|
|
17
|
+
if (typeof window === "undefined") return fallbackTheme;
|
|
18
|
+
|
|
19
|
+
try {
|
|
20
|
+
const storedTheme = window.localStorage.getItem(storageKey);
|
|
21
|
+
return isTheme(storedTheme) ? storedTheme : fallbackTheme;
|
|
22
|
+
} catch {
|
|
23
|
+
return fallbackTheme;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function getSystemTheme(): ResolvedTheme {
|
|
28
|
+
return window.matchMedia("(prefers-color-scheme: dark)").matches
|
|
29
|
+
? "dark"
|
|
30
|
+
: "light";
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function resolveTheme(theme: Theme): ResolvedTheme {
|
|
34
|
+
return theme === "system" ? getSystemTheme() : theme;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function applyTheme(theme: Theme): ResolvedTheme {
|
|
38
|
+
const resolvedTheme = resolveTheme(theme);
|
|
39
|
+
const root = window.document.documentElement;
|
|
40
|
+
|
|
41
|
+
root.classList.remove("light", "dark");
|
|
42
|
+
root.classList.add(resolvedTheme);
|
|
43
|
+
root.style.colorScheme = resolvedTheme;
|
|
44
|
+
|
|
45
|
+
return resolvedTheme;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function getThemeScript(storageKey: string, defaultTheme: Theme) {
|
|
49
|
+
return `(() => {
|
|
50
|
+
const storageKey = ${JSON.stringify(storageKey)};
|
|
51
|
+
const fallbackTheme = ${JSON.stringify(defaultTheme)};
|
|
52
|
+
const isTheme = (value) => value === "light" || value === "dark" || value === "system";
|
|
53
|
+
|
|
54
|
+
const storedTheme = (() => {
|
|
55
|
+
try {
|
|
56
|
+
const value = window.localStorage.getItem(storageKey);
|
|
57
|
+
return isTheme(value) ? value : fallbackTheme;
|
|
58
|
+
} catch {
|
|
59
|
+
return fallbackTheme;
|
|
60
|
+
}
|
|
61
|
+
})();
|
|
62
|
+
|
|
63
|
+
const resolvedTheme = storedTheme === "system"
|
|
64
|
+
? window.matchMedia("(prefers-color-scheme: dark)").matches
|
|
65
|
+
? "dark"
|
|
66
|
+
: "light"
|
|
67
|
+
: storedTheme;
|
|
68
|
+
|
|
69
|
+
const root = document.documentElement;
|
|
70
|
+
root.classList.remove("light", "dark");
|
|
71
|
+
root.classList.add(resolvedTheme);
|
|
72
|
+
root.style.colorScheme = resolvedTheme;
|
|
73
|
+
})();`;
|
|
74
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { AstroDocsApp } from "../components/docs-app";
|
|
3
|
+
import DocsLayout from "../layouts/docs-layout.astro";
|
|
4
|
+
import { docsContext, pathnameForSlug } from "../lib/docs";
|
|
5
|
+
import { docsSeo } from "../lib/seo";
|
|
6
|
+
import { openApiEndpointDetail } from "@heyo-sh/heyo-docs";
|
|
7
|
+
import { pages } from "virtual:heyo-docs-content";
|
|
8
|
+
import { openApiEndpoints } from "virtual:heyo-docs-openapi/index";
|
|
9
|
+
|
|
10
|
+
export async function getStaticPaths() {
|
|
11
|
+
return [...pages, ...openApiEndpoints].map(({ slug }) => ({
|
|
12
|
+
params: { slug: slug === "/" ? undefined : slug.slice(1) },
|
|
13
|
+
}));
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const pathname = pathnameForSlug(Astro.params.slug);
|
|
17
|
+
const context = docsContext(pathname);
|
|
18
|
+
const exists = Boolean(context.page || context.endpoint);
|
|
19
|
+
if (!exists) Astro.response.status = 404;
|
|
20
|
+
const currentOpenApiEndpoint = context.endpoint
|
|
21
|
+
? openApiEndpointDetail(context.endpoint)
|
|
22
|
+
: undefined;
|
|
23
|
+
|
|
24
|
+
const seo = exists
|
|
25
|
+
? docsSeo({ ...context, pathname })
|
|
26
|
+
: {
|
|
27
|
+
title: `Not found | ${context.config.title}`,
|
|
28
|
+
description: context.config.description,
|
|
29
|
+
structuredData: [
|
|
30
|
+
{
|
|
31
|
+
"@context": "https://schema.org",
|
|
32
|
+
"@type": "WebSite",
|
|
33
|
+
name: context.config.title,
|
|
34
|
+
description: context.config.description,
|
|
35
|
+
...(context.config.siteUrl ? { url: context.config.siteUrl } : {}),
|
|
36
|
+
},
|
|
37
|
+
],
|
|
38
|
+
};
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
<DocsLayout
|
|
42
|
+
{...seo}
|
|
43
|
+
robots={exists ? "index, follow" : "noindex"}
|
|
44
|
+
>
|
|
45
|
+
<AstroDocsApp
|
|
46
|
+
client:load
|
|
47
|
+
currentOpenApiEndpoint={currentOpenApiEndpoint}
|
|
48
|
+
pathname={pathname}
|
|
49
|
+
/>
|
|
50
|
+
</DocsLayout>
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { APIRoute } from "astro";
|
|
2
|
+
import { markdownForPage, pathnameFromMarkdownPath } from "@heyo-sh/heyo-docs";
|
|
3
|
+
|
|
4
|
+
import { pages } from "virtual:heyo-docs-content/server";
|
|
5
|
+
|
|
6
|
+
export function getStaticPaths() {
|
|
7
|
+
return pages.map((page) => ({
|
|
8
|
+
params: { slug: page.slug === "/" ? "index" : page.slug.slice(1) },
|
|
9
|
+
}));
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/** Serves every documentation page as stable, frontmatter-free Markdown. */
|
|
13
|
+
export const GET: APIRoute = ({ params }) => {
|
|
14
|
+
const pagePathname = pathnameFromMarkdownPath(`/${params.slug ?? ""}.md`);
|
|
15
|
+
const page = pagePathname
|
|
16
|
+
? pages.find((candidate) => candidate.slug === pagePathname)
|
|
17
|
+
: undefined;
|
|
18
|
+
|
|
19
|
+
if (!page)
|
|
20
|
+
return new Response("Not Found", {
|
|
21
|
+
status: 404,
|
|
22
|
+
headers: { "content-type": "text/plain; charset=utf-8" },
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
return new Response(markdownForPage(page), {
|
|
26
|
+
headers: { "content-type": "text/markdown; charset=utf-8" },
|
|
27
|
+
});
|
|
28
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { APIRoute } from "astro";
|
|
2
|
+
|
|
3
|
+
import { handleOpenApiRequest } from "../../lib/openapi-request";
|
|
4
|
+
|
|
5
|
+
/** The only dynamic OpenAPI surface: a same-origin proxy for Try it requests. */
|
|
6
|
+
export const prerender = false;
|
|
7
|
+
|
|
8
|
+
export const POST: APIRoute = ({ request }) => handleOpenApiRequest(request);
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { APIRoute } from "astro";
|
|
2
|
+
import { llmsFull } from "@heyo-sh/heyo-docs";
|
|
3
|
+
|
|
4
|
+
import config from "../../heyo-docs.config";
|
|
5
|
+
import { pages } from "virtual:heyo-docs-content/server";
|
|
6
|
+
|
|
7
|
+
/** The complete documentation corpus, ready for an LLM to consume. */
|
|
8
|
+
export const GET: APIRoute = ({ request }) => {
|
|
9
|
+
const siteUrl = config.siteUrl ?? new URL(request.url).origin;
|
|
10
|
+
return new Response(llmsFull(pages, siteUrl), {
|
|
11
|
+
headers: { "content-type": "text/plain; charset=utf-8" },
|
|
12
|
+
});
|
|
13
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { APIRoute } from "astro";
|
|
2
|
+
import { llmsIndex } from "@heyo-sh/heyo-docs";
|
|
3
|
+
|
|
4
|
+
import config from "../../heyo-docs.config";
|
|
5
|
+
import { pages } from "virtual:heyo-docs-content/server";
|
|
6
|
+
|
|
7
|
+
/** A concise, discoverable index of every documentation page. */
|
|
8
|
+
export const GET: APIRoute = ({ request }) => {
|
|
9
|
+
const siteUrl = config.siteUrl ?? new URL(request.url).origin;
|
|
10
|
+
return new Response(llmsIndex(pages, config, siteUrl), {
|
|
11
|
+
headers: { "content-type": "text/plain; charset=utf-8" },
|
|
12
|
+
});
|
|
13
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { APIRoute } from "astro";
|
|
2
|
+
|
|
3
|
+
import config from "../../heyo-docs.config";
|
|
4
|
+
|
|
5
|
+
export const GET: APIRoute = ({ request }) => {
|
|
6
|
+
const siteUrl = config.siteUrl ?? new URL(request.url).origin;
|
|
7
|
+
|
|
8
|
+
return new Response(
|
|
9
|
+
[
|
|
10
|
+
"User-agent: *",
|
|
11
|
+
"Allow: /",
|
|
12
|
+
"Disallow: /__heyo-docs/",
|
|
13
|
+
"",
|
|
14
|
+
`Sitemap: ${siteUrl}/sitemap.xml`,
|
|
15
|
+
"",
|
|
16
|
+
].join("\n"),
|
|
17
|
+
{ headers: { "content-type": "text/plain; charset=utf-8" } },
|
|
18
|
+
);
|
|
19
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { APIRoute } from "astro";
|
|
2
|
+
import { rssXml } from "@heyo-sh/heyo-docs";
|
|
3
|
+
|
|
4
|
+
import config from "../../heyo-docs.config";
|
|
5
|
+
import { pages } from "virtual:heyo-docs-content/server";
|
|
6
|
+
|
|
7
|
+
/** RSS 2.0 feed for every `<Update>` in the configured changelog groups. */
|
|
8
|
+
export const GET: APIRoute = ({ request }) => {
|
|
9
|
+
const siteUrl = config.siteUrl ?? new URL(request.url).origin;
|
|
10
|
+
return new Response(rssXml(pages, config, siteUrl), {
|
|
11
|
+
headers: { "content-type": "application/rss+xml; charset=utf-8" },
|
|
12
|
+
});
|
|
13
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { APIRoute } from "astro";
|
|
2
|
+
import { sitemapXml } from "@heyo-sh/heyo-docs";
|
|
3
|
+
|
|
4
|
+
import config from "../../heyo-docs.config";
|
|
5
|
+
import { createDocsModel } from "@heyo-sh/heyo-docs";
|
|
6
|
+
import { openApiDocuments } from "virtual:heyo-docs-openapi";
|
|
7
|
+
import { pages } from "virtual:heyo-docs-content";
|
|
8
|
+
|
|
9
|
+
export const GET: APIRoute = ({ request }) => {
|
|
10
|
+
const model = createDocsModel(config, pages, openApiDocuments);
|
|
11
|
+
const siteUrl = config.siteUrl ?? new URL(request.url).origin;
|
|
12
|
+
return new Response(
|
|
13
|
+
sitemapXml(
|
|
14
|
+
siteUrl,
|
|
15
|
+
[...model.pages, ...model.endpoints].map((page) => page.slug),
|
|
16
|
+
),
|
|
17
|
+
{ headers: { "content-type": "application/xml; charset=utf-8" } },
|
|
18
|
+
);
|
|
19
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
declare module "virtual:heyo-docs-content" {
|
|
2
|
+
import type { DocsPage } from "@heyo-sh/heyo-docs";
|
|
3
|
+
export const pages: DocsPage[];
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
declare module "virtual:heyo-docs-content/server" {
|
|
7
|
+
import type { MarkdownPage } from "@heyo-sh/heyo-docs";
|
|
8
|
+
export const pages: MarkdownPage[];
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
declare module "virtual:heyo-docs-openapi" {
|
|
12
|
+
import type { OpenApiDocumentSource } from "@heyo-sh/heyo-docs";
|
|
13
|
+
export const openApiDocuments: OpenApiDocumentSource[];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
declare module "virtual:heyo-docs-openapi/index" {
|
|
17
|
+
import type { OpenApiEndpoint } from "@heyo-sh/heyo-docs";
|
|
18
|
+
export const openApiEndpoints: OpenApiEndpoint[];
|
|
19
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "astro/tsconfigs/strict",
|
|
3
|
+
"include": [".astro/types.d.ts", "**/*"],
|
|
4
|
+
"exclude": ["dist"],
|
|
5
|
+
"compilerOptions": {
|
|
6
|
+
"jsx": "react-jsx",
|
|
7
|
+
"jsxImportSource": "react",
|
|
8
|
+
"allowJs": true,
|
|
9
|
+
"esModuleInterop": true,
|
|
10
|
+
"skipLibCheck": true
|
|
11
|
+
}
|
|
12
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { Metadata } from "next";
|
|
2
|
+
import { notFound } from "next/navigation";
|
|
3
|
+
import { openApiEndpointDetail } from "@heyo-sh/heyo-docs/node";
|
|
4
|
+
|
|
5
|
+
import { NextDocsApp } from "../components/docs-app";
|
|
6
|
+
import { docsContext, docsModel, pathnameForSegments } from "../lib/docs";
|
|
7
|
+
import { docsSeo } from "../lib/seo";
|
|
8
|
+
|
|
9
|
+
interface PageProps {
|
|
10
|
+
params: Promise<{ slug?: string[] }>;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/** Every known MDX and OpenAPI route is emitted as static HTML at build time. */
|
|
14
|
+
export const dynamic = "force-static";
|
|
15
|
+
|
|
16
|
+
export async function generateStaticParams() {
|
|
17
|
+
return [...docsModel.pages, ...docsModel.endpoints].map(({ slug }) => ({
|
|
18
|
+
slug: slug === "/" ? [] : slug.slice(1).split("/"),
|
|
19
|
+
}));
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export async function generateMetadata({
|
|
23
|
+
params,
|
|
24
|
+
}: PageProps): Promise<Metadata> {
|
|
25
|
+
const { slug } = await params;
|
|
26
|
+
const pathname = pathnameForSegments(slug);
|
|
27
|
+
const context = docsContext(pathname);
|
|
28
|
+
if (!context.page && !context.endpoint)
|
|
29
|
+
return {
|
|
30
|
+
title: `Not found | ${context.config.title}`,
|
|
31
|
+
robots: { index: false, follow: false },
|
|
32
|
+
};
|
|
33
|
+
return docsSeo({ ...context, pathname }).metadata;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export default async function DocsPage({ params }: PageProps) {
|
|
37
|
+
const { slug } = await params;
|
|
38
|
+
const pathname = pathnameForSegments(slug);
|
|
39
|
+
const context = docsContext(pathname);
|
|
40
|
+
if (!context.page && !context.endpoint) notFound();
|
|
41
|
+
const { structuredData } = docsSeo({ ...context, pathname });
|
|
42
|
+
|
|
43
|
+
return (
|
|
44
|
+
<>
|
|
45
|
+
<script
|
|
46
|
+
type="application/ld+json"
|
|
47
|
+
dangerouslySetInnerHTML={{
|
|
48
|
+
__html: JSON.stringify(structuredData).replace(/</g, "\\u003c"),
|
|
49
|
+
}}
|
|
50
|
+
/>
|
|
51
|
+
<NextDocsApp
|
|
52
|
+
currentOpenApiEndpoint={
|
|
53
|
+
context.endpoint ? openApiEndpointDetail(context.endpoint) : undefined
|
|
54
|
+
}
|
|
55
|
+
pathname={pathname}
|
|
56
|
+
/>
|
|
57
|
+
</>
|
|
58
|
+
);
|
|
59
|
+
}
|