@heyo-sh/create-heyo-docs 1.0.0 → 2.0.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 (122) hide show
  1. package/deployments/cloudflare/astro/package.json +2 -1
  2. package/deployments/cloudflare/astro/src/env.d.ts +8 -0
  3. package/deployments/cloudflare/astro/src/pages/heyo-docs-internal/ai-chat.ts +32 -0
  4. package/deployments/cloudflare/next/app/heyo-docs-internal/ai-chat/route.ts +27 -0
  5. package/deployments/cloudflare/next/cloudflare-env.d.ts +7 -0
  6. package/deployments/cloudflare/react-router/app/cloudflare-env.d.ts +7 -0
  7. package/deployments/cloudflare/react-router/app/lib/cloudflare-context.ts +4 -0
  8. package/deployments/cloudflare/react-router/app/routes/ai-chat.ts +31 -0
  9. package/deployments/cloudflare/react-router/package.json +2 -1
  10. package/deployments/cloudflare/react-router/workers/app.ts +6 -2
  11. package/deployments/vercel/astro/package.json +1 -0
  12. package/dist/index.js +37 -16
  13. package/dist/project-name.d.ts +1 -1
  14. package/dist/project-name.d.ts.map +1 -1
  15. package/dist/scaffold.d.ts.map +1 -1
  16. package/dist/utils.d.ts.map +1 -1
  17. package/package.json +2 -2
  18. package/templates/astro/content/index.mdx +8 -0
  19. package/templates/astro/heyo-docs.config.ts +4 -48
  20. package/templates/astro/src/components/docs-app.tsx +30 -130
  21. package/templates/astro/src/heyo-docs-icons.tsx +3 -1
  22. package/templates/astro/src/layouts/docs-layout.astro +20 -504
  23. package/templates/astro/src/lib/docs.ts +3 -2
  24. package/templates/astro/src/lib/openapi-request.ts +6 -146
  25. package/templates/astro/src/pages/[...slug].astro +7 -15
  26. package/templates/astro/src/pages/[...slug].md.ts +4 -1
  27. package/templates/astro/src/pages/heyo-docs-internal/ai-chat.ts +10 -3
  28. package/templates/astro/src/pages/heyo-docs-internal/openapi-request.ts +6 -0
  29. package/templates/astro/src/pages/index.astro +18 -9
  30. package/templates/astro/src/pages/llms-full.txt.ts +1 -1
  31. package/templates/astro/src/pages/llms.txt.ts +1 -1
  32. package/templates/astro/src/pages/rss.xml.ts +1 -1
  33. package/templates/astro/src/pages/sitemap.xml.ts +2 -2
  34. package/templates/astro/src/virtual.d.ts +5 -5
  35. package/templates/next/app/[[...slug]]/page.tsx +10 -9
  36. package/templates/next/app/components/docs-app.tsx +28 -120
  37. package/templates/next/app/heyo-docs-icons.tsx +1 -1
  38. package/templates/next/app/heyo-docs-internal/ai-chat/route.ts +9 -2
  39. package/templates/next/app/heyo-docs-internal/markdown/[[...slug]]/route.ts +1 -1
  40. package/templates/next/app/layout.tsx +25 -444
  41. package/templates/next/app/lib/docs.ts +3 -3
  42. package/templates/next/app/lib/openapi-request.ts +4 -136
  43. package/templates/next/app/llms-full.txt/route.ts +1 -1
  44. package/templates/next/app/llms.txt/route.ts +1 -1
  45. package/templates/next/app/rss.xml/route.ts +1 -1
  46. package/templates/next/app/sitemap.xml/route.ts +1 -1
  47. package/templates/next/content/index.mdx +8 -0
  48. package/templates/next/heyo-docs.config.ts +3 -47
  49. package/templates/next/next.config.ts +7 -1
  50. package/templates/react-router/app/heyo-docs-icons.tsx +3 -1
  51. package/templates/react-router/app/root.tsx +28 -465
  52. package/templates/react-router/app/routes/ai-chat.ts +6 -2
  53. package/templates/react-router/app/routes/docs.tsx +22 -63
  54. package/templates/react-router/app/routes/home.tsx +6 -1
  55. package/templates/react-router/app/routes/llms-full.ts +1 -1
  56. package/templates/react-router/app/routes/llms.ts +1 -1
  57. package/templates/react-router/app/routes/markdown.ts +4 -1
  58. package/templates/react-router/app/routes/openapi-request.ts +11 -144
  59. package/templates/react-router/app/routes/rss.ts +1 -1
  60. package/templates/react-router/app/routes/sitemap.ts +5 -8
  61. package/templates/react-router/app/virtual.d.ts +5 -5
  62. package/templates/react-router/content/index.mdx +8 -0
  63. package/templates/react-router/heyo-docs.config.ts +4 -48
  64. package/templates/astro/content/api-overview.mdx +0 -20
  65. package/templates/astro/content/assets/local-architecture.svg +0 -22
  66. package/templates/astro/content/assets/quick-reference.txt +0 -5
  67. package/templates/astro/content/changelog.mdx +0 -38
  68. package/templates/astro/content/configuration.mdx +0 -117
  69. package/templates/astro/content/deploying.mdx +0 -57
  70. package/templates/astro/content/essentials/callouts.mdx +0 -34
  71. package/templates/astro/content/essentials/code-blocks.mdx +0 -43
  72. package/templates/astro/content/essentials/lists.mdx +0 -36
  73. package/templates/astro/content/essentials/tables.mdx +0 -30
  74. package/templates/astro/content/essentials/tabs.mdx +0 -40
  75. package/templates/astro/content/essentials/text-styling.mdx +0 -43
  76. package/templates/astro/content/getting-started.mdx +0 -16
  77. package/templates/astro/content/mdx-components.mdx +0 -44
  78. package/templates/astro/content/openapi-petstore.json +0 -160
  79. package/templates/astro/content/openapi.json +0 -278
  80. package/templates/astro/content/quickstart.mdx +0 -60
  81. package/templates/astro/src/components/theme-provider.tsx +0 -106
  82. package/templates/astro/src/lib/seo.ts +0 -221
  83. package/templates/astro/src/lib/theme.ts +0 -74
  84. package/templates/next/app/components/theme-provider.tsx +0 -93
  85. package/templates/next/app/lib/seo.ts +0 -189
  86. package/templates/next/app/lib/theme.ts +0 -61
  87. package/templates/next/content/api-overview.mdx +0 -20
  88. package/templates/next/content/assets/local-architecture.svg +0 -22
  89. package/templates/next/content/assets/quick-reference.txt +0 -5
  90. package/templates/next/content/changelog.mdx +0 -38
  91. package/templates/next/content/configuration.mdx +0 -117
  92. package/templates/next/content/deploying.mdx +0 -57
  93. package/templates/next/content/essentials/callouts.mdx +0 -34
  94. package/templates/next/content/essentials/code-blocks.mdx +0 -43
  95. package/templates/next/content/essentials/lists.mdx +0 -36
  96. package/templates/next/content/essentials/tables.mdx +0 -30
  97. package/templates/next/content/essentials/tabs.mdx +0 -40
  98. package/templates/next/content/essentials/text-styling.mdx +0 -43
  99. package/templates/next/content/getting-started.mdx +0 -16
  100. package/templates/next/content/mdx-components.mdx +0 -41
  101. package/templates/next/content/openapi-petstore.json +0 -160
  102. package/templates/next/content/openapi.json +0 -278
  103. package/templates/next/content/quickstart.mdx +0 -60
  104. package/templates/react-router/app/components/theme-provider.tsx +0 -106
  105. package/templates/react-router/app/lib/theme.ts +0 -74
  106. package/templates/react-router/content/api-overview.mdx +0 -20
  107. package/templates/react-router/content/assets/local-architecture.svg +0 -22
  108. package/templates/react-router/content/assets/quick-reference.txt +0 -5
  109. package/templates/react-router/content/changelog.mdx +0 -38
  110. package/templates/react-router/content/configuration.mdx +0 -117
  111. package/templates/react-router/content/deploying.mdx +0 -57
  112. package/templates/react-router/content/essentials/callouts.mdx +0 -34
  113. package/templates/react-router/content/essentials/code-blocks.mdx +0 -43
  114. package/templates/react-router/content/essentials/lists.mdx +0 -36
  115. package/templates/react-router/content/essentials/tables.mdx +0 -30
  116. package/templates/react-router/content/essentials/tabs.mdx +0 -40
  117. package/templates/react-router/content/essentials/text-styling.mdx +0 -43
  118. package/templates/react-router/content/getting-started.mdx +0 -16
  119. package/templates/react-router/content/mdx-components.mdx +0 -293
  120. package/templates/react-router/content/openapi-petstore.json +0 -160
  121. package/templates/react-router/content/openapi.json +0 -278
  122. package/templates/react-router/content/quickstart.mdx +0 -60
@@ -1,278 +0,0 @@
1
- {
2
- "openapi": "3.1.0",
3
- "info": {
4
- "title": "Planet API",
5
- "version": "1.0.0",
6
- "description": "An example API used by the Heyo Docs React Router demo."
7
- },
8
- "servers": [
9
- {
10
- "url": "https://api.example.com/v1",
11
- "description": "Example production API"
12
- }
13
- ],
14
- "security": [
15
- {
16
- "BearerAuth": []
17
- }
18
- ],
19
- "paths": {
20
- "/organizations/{organizationId}/planets": {
21
- "parameters": [
22
- {
23
- "name": "organizationId",
24
- "in": "path",
25
- "required": true,
26
- "description": "The organization that owns the planets.",
27
- "schema": {
28
- "type": "string",
29
- "example": "org_123"
30
- }
31
- }
32
- ],
33
- "get": {
34
- "operationId": "listPlanets",
35
- "summary": "List planets",
36
- "description": "Returns the planets that belong to an organization.",
37
- "tags": ["Planets"],
38
- "parameters": [
39
- {
40
- "name": "limit",
41
- "in": "query",
42
- "description": "Maximum number of records to return.",
43
- "schema": {
44
- "type": "integer",
45
- "default": 20
46
- }
47
- }
48
- ],
49
- "responses": {
50
- "200": {
51
- "description": "A page of planets.",
52
- "content": {
53
- "application/json": {
54
- "schema": {
55
- "type": "array",
56
- "items": {
57
- "$ref": "#/components/schemas/Planet"
58
- }
59
- }
60
- }
61
- }
62
- }
63
- }
64
- },
65
- "post": {
66
- "operationId": "createPlanet",
67
- "summary": "Create a planet",
68
- "description": "Creates a planet inside an organization.",
69
- "tags": ["Planets"],
70
- "requestBody": {
71
- "required": true,
72
- "description": "The planet to create.",
73
- "content": {
74
- "application/json": {
75
- "schema": {
76
- "$ref": "#/components/schemas/PlanetInput"
77
- }
78
- }
79
- }
80
- },
81
- "responses": {
82
- "201": {
83
- "description": "The newly created planet.",
84
- "content": {
85
- "application/json": {
86
- "schema": {
87
- "$ref": "#/components/schemas/Planet"
88
- }
89
- }
90
- }
91
- }
92
- }
93
- }
94
- },
95
- "/organizations/{organizationId}/planets/{planetId}": {
96
- "parameters": [
97
- {
98
- "name": "organizationId",
99
- "in": "path",
100
- "required": true,
101
- "description": "The organization that owns the planet.",
102
- "schema": {
103
- "type": "string",
104
- "example": "org_123"
105
- }
106
- },
107
- {
108
- "name": "planetId",
109
- "in": "path",
110
- "required": true,
111
- "description": "The planet identifier.",
112
- "schema": {
113
- "type": "string",
114
- "example": "planet_earth"
115
- }
116
- }
117
- ],
118
- "get": {
119
- "operationId": "getPlanet",
120
- "summary": "Get a planet",
121
- "description": "Returns one planet by ID.",
122
- "tags": ["Planets"],
123
- "responses": {
124
- "200": {
125
- "description": "The requested planet.",
126
- "content": {
127
- "application/json": {
128
- "schema": {
129
- "$ref": "#/components/schemas/Planet"
130
- }
131
- }
132
- }
133
- },
134
- "404": {
135
- "description": "Planet not found."
136
- }
137
- }
138
- },
139
- "put": {
140
- "operationId": "replacePlanet",
141
- "summary": "Replace a planet",
142
- "description": "Replaces all editable planet values.",
143
- "tags": ["Planets"],
144
- "requestBody": {
145
- "required": true,
146
- "content": {
147
- "application/json": {
148
- "schema": {
149
- "$ref": "#/components/schemas/PlanetInput"
150
- }
151
- }
152
- }
153
- },
154
- "responses": {
155
- "200": {
156
- "description": "The updated planet.",
157
- "content": {
158
- "application/json": {
159
- "schema": {
160
- "$ref": "#/components/schemas/Planet"
161
- }
162
- }
163
- }
164
- }
165
- }
166
- },
167
- "patch": {
168
- "operationId": "updatePlanet",
169
- "summary": "Update a planet",
170
- "description": "Updates one or more planet fields.",
171
- "tags": ["Planets"],
172
- "requestBody": {
173
- "required": true,
174
- "content": {
175
- "application/json": {
176
- "schema": {
177
- "$ref": "#/components/schemas/PlanetPatch"
178
- }
179
- }
180
- }
181
- },
182
- "responses": {
183
- "200": {
184
- "description": "The updated planet.",
185
- "content": {
186
- "application/json": {
187
- "schema": {
188
- "$ref": "#/components/schemas/Planet"
189
- }
190
- }
191
- }
192
- }
193
- }
194
- },
195
- "delete": {
196
- "operationId": "deletePlanet",
197
- "summary": "Delete a planet",
198
- "description": "Permanently removes a planet from an organization.",
199
- "tags": ["Planets"],
200
- "parameters": [
201
- {
202
- "name": "hardDelete",
203
- "in": "query",
204
- "description": "Also remove historical records.",
205
- "schema": {
206
- "type": "boolean",
207
- "default": false
208
- }
209
- }
210
- ],
211
- "responses": {
212
- "204": {
213
- "description": "Planet deleted."
214
- },
215
- "404": {
216
- "description": "Planet not found."
217
- }
218
- }
219
- }
220
- }
221
- },
222
- "components": {
223
- "securitySchemes": {
224
- "BearerAuth": {
225
- "type": "http",
226
- "scheme": "bearer",
227
- "bearerFormat": "JWT"
228
- }
229
- },
230
- "schemas": {
231
- "Planet": {
232
- "type": "object",
233
- "required": ["id", "name", "classification"],
234
- "properties": {
235
- "id": {
236
- "type": "string",
237
- "example": "planet_earth"
238
- },
239
- "name": {
240
- "type": "string",
241
- "example": "Earth"
242
- },
243
- "classification": {
244
- "type": "string",
245
- "enum": ["terrestrial", "gas-giant", "ice-giant"]
246
- }
247
- }
248
- },
249
- "PlanetInput": {
250
- "type": "object",
251
- "required": ["name", "classification"],
252
- "properties": {
253
- "name": {
254
- "type": "string",
255
- "example": "Mars"
256
- },
257
- "classification": {
258
- "type": "string",
259
- "enum": ["terrestrial", "gas-giant", "ice-giant"]
260
- }
261
- }
262
- },
263
- "PlanetPatch": {
264
- "type": "object",
265
- "properties": {
266
- "name": {
267
- "type": "string",
268
- "example": "New Mars"
269
- },
270
- "classification": {
271
- "type": "string",
272
- "enum": ["terrestrial", "gas-giant", "ice-giant"]
273
- }
274
- }
275
- }
276
- }
277
- }
278
- }
@@ -1,60 +0,0 @@
1
- ---
2
- title: Quickstart
3
- description: Create, run, and understand your Heyo Docs project.
4
- ---
5
-
6
- # Quickstart
7
-
8
- A new Heyo Docs project is a complete documentation site: MDX files provide the
9
- content, `heyo-docs.config.ts` defines the site model, and your selected
10
- framework renders the finished experience.
11
-
12
- ## Create a project
13
-
14
- Use the creator with the package manager you already use. It asks for a project
15
- name, framework, deployment target, theme, and package manager, then creates a
16
- ready-to-run application.
17
-
18
- <CodeBlockGroup defaultValue="bun" variant="line">
19
- <CodeBlock language="bash" title="bun">
20
- {"bun create @heyo-sh/heyo-docs"}
21
- </CodeBlock>
22
- <CodeBlock language="bash" title="npm">
23
- {"npm create @heyo-sh/heyo-docs@latest"}
24
- </CodeBlock>
25
- <CodeBlock language="bash" title="pnpm">
26
- {"pnpm create @heyo-sh/heyo-docs"}
27
- </CodeBlock>
28
- <CodeBlock language="bash" title="yarn">
29
- {"yarn dlx @heyo-sh/create-heyo-docs"}
30
- </CodeBlock>
31
- </CodeBlockGroup>
32
-
33
- When the command finishes, move into the new directory and run the development
34
- command printed by the creator. The local server rebuilds the documentation
35
- when you change either content or configuration.
36
-
37
- ## How the pieces fit together
38
-
39
- 1. **Content** lives in the `content` directory. Every `.mdx` file becomes a
40
- documentation route.
41
- 2. **Configuration** in `heyo-docs.config.ts` gives the site its name, theme,
42
- navigation order, and public metadata.
43
- 3. **The framework app** loads the same model during development and production
44
- builds, so the sidebar, search, routes, and adjacent-page links stay in sync.
45
-
46
- Create a new file such as `content/guides/deployment.mdx` to add the
47
- `/guides/deployment` route. Add that path to a navigation section when the
48
- page should appear in the sidebar.
49
-
50
- ## Make your first change
51
-
52
- Update the title and description in the frontmatter of `content/index.mdx`,
53
- then refresh the browser. Use [Basic Configuration](/configuration) to adjust
54
- the site model and [Text Styling](/essentials/text-styling) for writing the
55
- page itself.
56
-
57
- <Callout type="tip" title="One source of truth">
58
- Keep navigation in <code>heyo-docs.config.ts</code> and page-specific details
59
- in MDX frontmatter. The build validates both before publishing.
60
- </Callout>
@@ -1,106 +0,0 @@
1
- import {
2
- createContext,
3
- useContext,
4
- useEffect,
5
- useState,
6
- type ReactNode,
7
- } from "react";
8
-
9
- import {
10
- applyTheme,
11
- DEFAULT_THEME,
12
- getStoredTheme,
13
- type ResolvedTheme,
14
- THEME_STORAGE_KEY,
15
- type Theme,
16
- } from "../lib/theme";
17
-
18
- type ThemeProviderProps = {
19
- children: ReactNode;
20
- defaultTheme?: Theme;
21
- storageKey?: string;
22
- };
23
-
24
- type ThemeProviderState = {
25
- theme: Theme;
26
- resolvedTheme: ResolvedTheme;
27
- mounted: boolean;
28
- setTheme: (theme: Theme) => void;
29
- };
30
-
31
- const ThemeProviderContext = createContext<ThemeProviderState | undefined>(
32
- undefined,
33
- );
34
-
35
- export function ThemeProvider({
36
- children,
37
- defaultTheme = DEFAULT_THEME,
38
- storageKey = THEME_STORAGE_KEY,
39
- }: ThemeProviderProps) {
40
- const [theme, setThemeState] = useState<Theme>(defaultTheme);
41
- const [resolvedTheme, setResolvedTheme] = useState<ResolvedTheme>(
42
- defaultTheme === "system" ? "light" : defaultTheme,
43
- );
44
- const [mounted, setMounted] = useState(false);
45
-
46
- useEffect(() => {
47
- const syncTheme = () => {
48
- const nextTheme = getStoredTheme(storageKey, defaultTheme);
49
-
50
- setThemeState(nextTheme);
51
- setResolvedTheme(applyTheme(nextTheme));
52
- };
53
-
54
- syncTheme();
55
-
56
- const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
57
- const handleSystemThemeChange = () => {
58
- const nextTheme = getStoredTheme(storageKey, defaultTheme);
59
- setResolvedTheme(applyTheme(nextTheme));
60
- };
61
- const handleStorage = (event: StorageEvent) => {
62
- if (event.key !== null && event.key !== storageKey) return;
63
- syncTheme();
64
- };
65
-
66
- mediaQuery.addEventListener("change", handleSystemThemeChange);
67
- window.addEventListener("storage", handleStorage);
68
- setMounted(true);
69
-
70
- return () => {
71
- mediaQuery.removeEventListener("change", handleSystemThemeChange);
72
- window.removeEventListener("storage", handleStorage);
73
- };
74
- }, [defaultTheme, storageKey]);
75
-
76
- const value = {
77
- theme,
78
- resolvedTheme,
79
- mounted,
80
- setTheme: (nextTheme: Theme) => {
81
- try {
82
- window.localStorage.setItem(storageKey, nextTheme);
83
- } catch {
84
- // The requested preference still applies when storage is unavailable.
85
- }
86
-
87
- setThemeState(nextTheme);
88
- setResolvedTheme(applyTheme(nextTheme));
89
- },
90
- };
91
-
92
- return (
93
- <ThemeProviderContext.Provider value={value}>
94
- {children}
95
- </ThemeProviderContext.Provider>
96
- );
97
- }
98
-
99
- export function useTheme() {
100
- const context = useContext(ThemeProviderContext);
101
-
102
- if (context === undefined)
103
- throw new Error("useTheme must be used within a ThemeProvider");
104
-
105
- return context;
106
- }
@@ -1,221 +0,0 @@
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
- }