@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.
Files changed (146) hide show
  1. package/LICENSE +21 -0
  2. package/deployments/cloudflare/astro/package.json +12 -0
  3. package/deployments/cloudflare/astro/platform.astro.ts +4 -0
  4. package/deployments/cloudflare/astro/wrangler.jsonc +6 -0
  5. package/deployments/cloudflare/next/open-next.config.ts +3 -0
  6. package/deployments/cloudflare/next/package.json +13 -0
  7. package/deployments/cloudflare/next/wrangler.jsonc +11 -0
  8. package/deployments/cloudflare/react-router/app/entry.server.tsx +48 -0
  9. package/deployments/cloudflare/react-router/package.json +21 -0
  10. package/deployments/cloudflare/react-router/platform.vite.ts +6 -0
  11. package/deployments/cloudflare/react-router/react-router.config.ts +20 -0
  12. package/deployments/cloudflare/react-router/workers/app.ts +12 -0
  13. package/deployments/cloudflare/react-router/wrangler.jsonc +10 -0
  14. package/deployments/vercel/astro/package.json +11 -0
  15. package/deployments/vercel/astro/platform.astro.ts +4 -0
  16. package/deployments/vercel/astro/vercel.json +4 -0
  17. package/deployments/vercel/next/package.json +8 -0
  18. package/deployments/vercel/next/vercel.json +3 -0
  19. package/deployments/vercel/react-router/package.json +12 -0
  20. package/deployments/vercel/react-router/platform.vite.ts +4 -0
  21. package/deployments/vercel/react-router/react-router.config.ts +22 -0
  22. package/deployments/vercel/react-router/vercel.json +4 -0
  23. package/dist/index.d.ts +13 -0
  24. package/dist/index.d.ts.map +1 -0
  25. package/dist/index.js +328 -0
  26. package/dist/package-manager.d.ts +6 -0
  27. package/dist/package-manager.d.ts.map +1 -0
  28. package/dist/project-name.d.ts +3 -0
  29. package/dist/project-name.d.ts.map +1 -0
  30. package/dist/scaffold.d.ts +5 -0
  31. package/dist/scaffold.d.ts.map +1 -0
  32. package/dist/types.d.ts +19 -0
  33. package/dist/types.d.ts.map +1 -0
  34. package/dist/utils.d.ts +8 -0
  35. package/dist/utils.d.ts.map +1 -0
  36. package/package.json +36 -0
  37. package/templates/astro/astro.config.ts +15 -0
  38. package/templates/astro/components.json +25 -0
  39. package/templates/astro/content/getting-started.mdx +16 -0
  40. package/templates/astro/content/index.mdx +22 -0
  41. package/templates/astro/content/mdx-components.mdx +44 -0
  42. package/templates/astro/heyo-docs.config.ts +24 -0
  43. package/templates/astro/package.json +34 -0
  44. package/templates/astro/platform.astro.ts +4 -0
  45. package/templates/astro/public/apple-touch-icon.png +0 -0
  46. package/templates/astro/public/favicon-96x96.png +0 -0
  47. package/templates/astro/public/favicon.ico +0 -0
  48. package/templates/astro/public/favicon.svg +1 -0
  49. package/templates/astro/public/logo.svg +12 -0
  50. package/templates/astro/public/site.webmanifest +21 -0
  51. package/templates/astro/public/web-app-manifest-192x192.png +0 -0
  52. package/templates/astro/public/web-app-manifest-512x512.png +0 -0
  53. package/templates/astro/src/components/docs-app.tsx +165 -0
  54. package/templates/astro/src/components/theme-provider.tsx +106 -0
  55. package/templates/astro/src/env.d.ts +2 -0
  56. package/templates/astro/src/heyo-docs-icons.tsx +79 -0
  57. package/templates/astro/src/layouts/docs-layout.astro +70 -0
  58. package/templates/astro/src/lib/docs.ts +31 -0
  59. package/templates/astro/src/lib/openapi-request.ts +160 -0
  60. package/templates/astro/src/lib/seo.ts +221 -0
  61. package/templates/astro/src/lib/theme.ts +74 -0
  62. package/templates/astro/src/pages/[...slug].astro +50 -0
  63. package/templates/astro/src/pages/[...slug].md.ts +28 -0
  64. package/templates/astro/src/pages/heyo-docs-internal/openapi-request.ts +8 -0
  65. package/templates/astro/src/pages/llms-full.txt.ts +13 -0
  66. package/templates/astro/src/pages/llms.txt.ts +13 -0
  67. package/templates/astro/src/pages/robots.txt.ts +19 -0
  68. package/templates/astro/src/pages/rss.xml.ts +13 -0
  69. package/templates/astro/src/pages/sitemap.xml.ts +19 -0
  70. package/templates/astro/src/styles/app.css +8 -0
  71. package/templates/astro/src/virtual.d.ts +19 -0
  72. package/templates/astro/tsconfig.json +12 -0
  73. package/templates/next/app/[[...slug]]/page.tsx +59 -0
  74. package/templates/next/app/app.css +8 -0
  75. package/templates/next/app/components/docs-app.tsx +139 -0
  76. package/templates/next/app/components/theme-provider.tsx +93 -0
  77. package/templates/next/app/error.tsx +8 -0
  78. package/templates/next/app/heyo-docs-icons.tsx +70 -0
  79. package/templates/next/app/heyo-docs-internal/markdown/[[...slug]]/route.ts +26 -0
  80. package/templates/next/app/heyo-docs-internal/openapi-request/route.ts +5 -0
  81. package/templates/next/app/layout.tsx +79 -0
  82. package/templates/next/app/lib/docs.ts +43 -0
  83. package/templates/next/app/lib/openapi-request.ts +147 -0
  84. package/templates/next/app/lib/seo.ts +189 -0
  85. package/templates/next/app/lib/theme.ts +61 -0
  86. package/templates/next/app/llms-full.txt/route.ts +10 -0
  87. package/templates/next/app/llms.txt/route.ts +10 -0
  88. package/templates/next/app/not-found.tsx +13 -0
  89. package/templates/next/app/robots.txt/route.ts +16 -0
  90. package/templates/next/app/rss.xml/route.ts +10 -0
  91. package/templates/next/app/sitemap.xml/route.ts +14 -0
  92. package/templates/next/components.json +20 -0
  93. package/templates/next/content/getting-started.mdx +16 -0
  94. package/templates/next/content/index.mdx +22 -0
  95. package/templates/next/content/mdx-components.mdx +41 -0
  96. package/templates/next/heyo-docs.config.ts +24 -0
  97. package/templates/next/mdx.d.ts +6 -0
  98. package/templates/next/next-env.d.ts +7 -0
  99. package/templates/next/next.config.ts +23 -0
  100. package/templates/next/package.json +40 -0
  101. package/templates/next/postcss.config.mjs +5 -0
  102. package/templates/next/public/apple-touch-icon.png +0 -0
  103. package/templates/next/public/favicon-96x96.png +0 -0
  104. package/templates/next/public/favicon.ico +0 -0
  105. package/templates/next/public/favicon.svg +1 -0
  106. package/templates/next/public/logo.svg +12 -0
  107. package/templates/next/public/site.webmanifest +21 -0
  108. package/templates/next/public/web-app-manifest-192x192.png +0 -0
  109. package/templates/next/public/web-app-manifest-512x512.png +0 -0
  110. package/templates/next/scripts/generate-heyo-docs.ts +50 -0
  111. package/templates/next/tsconfig.json +27 -0
  112. package/templates/react-router/app/app.css +8 -0
  113. package/templates/react-router/app/components/theme-provider.tsx +106 -0
  114. package/templates/react-router/app/heyo-docs-icons.tsx +71 -0
  115. package/templates/react-router/app/lib/theme.ts +74 -0
  116. package/templates/react-router/app/root.tsx +140 -0
  117. package/templates/react-router/app/routes/docs.tsx +179 -0
  118. package/templates/react-router/app/routes/home.tsx +1 -0
  119. package/templates/react-router/app/routes/llms-full.ts +13 -0
  120. package/templates/react-router/app/routes/llms.ts +13 -0
  121. package/templates/react-router/app/routes/markdown.ts +25 -0
  122. package/templates/react-router/app/routes/openapi-request.ts +159 -0
  123. package/templates/react-router/app/routes/page.tsx +1 -0
  124. package/templates/react-router/app/routes/robots.ts +19 -0
  125. package/templates/react-router/app/routes/rss.ts +13 -0
  126. package/templates/react-router/app/routes/sitemap.ts +18 -0
  127. package/templates/react-router/app/routes.ts +13 -0
  128. package/templates/react-router/app/virtual.d.ts +19 -0
  129. package/templates/react-router/components.json +25 -0
  130. package/templates/react-router/content/getting-started.mdx +16 -0
  131. package/templates/react-router/content/index.mdx +22 -0
  132. package/templates/react-router/content/mdx-components.mdx +293 -0
  133. package/templates/react-router/heyo-docs.config.ts +24 -0
  134. package/templates/react-router/package.json +36 -0
  135. package/templates/react-router/platform.vite.ts +2 -0
  136. package/templates/react-router/public/apple-touch-icon.png +0 -0
  137. package/templates/react-router/public/favicon-96x96.png +0 -0
  138. package/templates/react-router/public/favicon.ico +0 -0
  139. package/templates/react-router/public/favicon.svg +1 -0
  140. package/templates/react-router/public/logo.svg +12 -0
  141. package/templates/react-router/public/site.webmanifest +21 -0
  142. package/templates/react-router/public/web-app-manifest-192x192.png +0 -0
  143. package/templates/react-router/public/web-app-manifest-512x512.png +0 -0
  144. package/templates/react-router/react-router.config.ts +20 -0
  145. package/templates/react-router/tsconfig.json +20 -0
  146. 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,8 @@
1
+ @import "@fontsource-variable/figtree";
2
+ @import "@heyo-sh/heyo-docs/theme/{{THEME}}.css";
3
+
4
+ :root {
5
+ --heyo-docs-font-family: "Figtree Variable", sans-serif;
6
+ }
7
+
8
+ /* Add project-specific token overrides and theme styles below this import. */
@@ -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
+ }
@@ -0,0 +1,8 @@
1
+ @import "@fontsource-variable/figtree";
2
+ @import "@heyo-sh/heyo-docs/theme/{{THEME}}.css";
3
+
4
+ :root {
5
+ --heyo-docs-font-family: "Figtree Variable", sans-serif;
6
+ }
7
+
8
+ /* Add project-specific token overrides and theme styles below this import. */