Skip to content

Commit f76ebe0

Browse files
authored
feat: configure nuxt-llms module (#14)
1 parent 348ee4b commit f76ebe0

7 files changed

Lines changed: 171 additions & 77 deletions

File tree

‎nuxt.config.ts‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ export default defineNuxtConfig({
1212
'@nuxtjs/sitemap',
1313
'nuxt-seo-utils',
1414
'nuxt-og-image',
15-
'@nuxtjs/mcp-toolkit'
15+
'@nuxtjs/mcp-toolkit',
16+
'nuxt-llms',
1617
],
1718
ignore: ['content/**'],
1819
ui: { content: true, prose: true },
@@ -40,6 +41,10 @@ export default defineNuxtConfig({
4041
},
4142
},
4243
nitro: {
44+
routeRules: {
45+
'/llms.txt': { prerender: false },
46+
'/llms-full.txt': { prerender: false },
47+
},
4348
vercel: {
4449
config: {
4550
bypassToken: process.env.VERCEL_BYPASS_TOKEN,

‎package.json‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
"dependencies": {
3737
"@ai-sdk/gateway": "^4.0.16",
3838
"@ai-sdk/vue": "^4.0.22",
39+
3940
"@comark/nuxt": "^0.6.2",
4041
"@iconify-json/lucide": "^1.2.120",
4142
"@iconify-json/simple-icons": "^1.2.92",
@@ -56,11 +57,13 @@
5657
"@vueuse/core": "^14.3.0",
5758
"ai": "^7.0.22",
5859
"beautiful-mermaid": "^1.1.3",
60+
5961
"comark": "^0.6.2",
6062
"comark-content": "^0.3.0",
6163
"defu": "^6.1.7",
6264
"exsolve": "^1.1.0",
6365
"motion-v": "^2.3.0",
66+
"nuxt-llms": "^0.2.0",
6467
"nuxt-og-image": "^6.7.4",
6568
"nuxt-seo-utils": "^8.3.2",
6669
"pathe": "^2.0.3",

‎playground/nuxt.config.ts‎

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,12 @@ export default defineNuxtConfig({
33
site: {
44
url: 'https://docs.comark.dev',
55
name: 'Comark Docs',
6-
}
6+
},
7+
llms: {
8+
domain: 'https://docs.comark.dev',
9+
full: {
10+
title: 'Full documentation',
11+
description: 'All pages concatenated in one file',
12+
},
13+
},
714
})

‎pnpm-lock.yaml‎

Lines changed: 16 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎server/plugins/llms.ts‎

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
import { appendHeader } from 'h3'
2+
import type { NavigationItem } from 'comark-content'
3+
import { useAppConfig } from 'nitropack/runtime'
4+
import type { NitroApp } from 'nitropack/types'
5+
import { joinURL } from 'ufo'
6+
import type { LLMsSection } from 'nuxt-llms'
7+
8+
export default defineNitroPlugin((nitroApp: NitroApp) => {
9+
const prerenderPaths = new Set<string>()
10+
11+
nitroApp.hooks.hook('llms:generate', async (event, options) => {
12+
const content = await getProdContent()
13+
const navigation = await content.navigation()
14+
const site = getSiteConfig(event)
15+
const appConfig = useAppConfig(event)
16+
const siteName = appConfig.seo?.siteName || site.name || options.title || ''
17+
const siteUrl = options.domain || site.url || '/'
18+
19+
options.title ||= siteName
20+
options.description ||= appConfig.docs?.llms?.description || site.description || ''
21+
22+
const introLinks = [
23+
{
24+
title: 'Landing page',
25+
description: `Overview of ${siteName}`,
26+
href: documentLink('/', siteUrl, prerenderPaths),
27+
},
28+
...standaloneLinks(navigation, siteUrl, prerenderPaths),
29+
]
30+
31+
options.sections.unshift({
32+
title: 'Documentation',
33+
description: 'Every page below is available as raw markdown. Fetch any URL directly.',
34+
links: introLinks,
35+
})
36+
37+
for (const item of navigation) {
38+
if (!item.children?.length) continue
39+
40+
const links = sectionLinks(item, siteUrl, prerenderPaths)
41+
if (!links.length) continue
42+
43+
options.sections.push({
44+
title: item.title,
45+
description: item.description,
46+
links,
47+
})
48+
}
49+
50+
const extraLinks = (appConfig.docs?.llms?.links ?? []) as LLMsSection['links']
51+
if (extraLinks?.length) {
52+
options.sections.push({
53+
title: 'Optional',
54+
links: extraLinks,
55+
})
56+
}
57+
})
58+
59+
nitroApp.hooks.hook('llms:generate:full', async (_event, _options, contents) => {
60+
const content = await getProdContent()
61+
const navigation = await content.navigation()
62+
63+
const paths: string[] = []
64+
const collect = (items: NavigationItem[]) => {
65+
for (const item of items) {
66+
if (item.page !== false && item.path && item.path !== '/') paths.push(item.path)
67+
if (item.children?.length) collect(item.children)
68+
}
69+
}
70+
collect(navigation)
71+
72+
const pages = await Promise.all(
73+
[...new Set(paths)].map((path) => renderPageMarkdown(content, path))
74+
)
75+
contents.push(...pages.filter((page): page is string => Boolean(page)))
76+
})
77+
78+
if (['nitro-prerender', 'nitro-dev'].includes(import.meta.preset as string)) {
79+
nitroApp.hooks.hook('beforeResponse', (event) => {
80+
if (event.path === '/') {
81+
appendHeader(event, 'x-nitro-prerender', Array.from(prerenderPaths))
82+
}
83+
})
84+
}
85+
})
86+
87+
function standaloneLinks(
88+
navigation: NavigationItem[],
89+
siteUrl: string,
90+
prerenderPaths: Set<string>
91+
): NonNullable<LLMsSection['links']> {
92+
return navigation
93+
.filter((item) => !item.children?.length && item.page !== false && item.path && item.path !== '/')
94+
.map((item) => ({
95+
title: item.title,
96+
description: item.description,
97+
href: documentLink(item.path, siteUrl, prerenderPaths),
98+
}))
99+
}
100+
101+
function sectionLinks(
102+
item: NavigationItem,
103+
siteUrl: string,
104+
prerenderPaths: Set<string>
105+
): NonNullable<LLMsSection['links']> {
106+
const links: NonNullable<LLMsSection['links']> = []
107+
108+
const collect = (items: NavigationItem[]) => {
109+
for (const entry of items) {
110+
if (entry.page !== false && entry.path && entry.path !== '/') {
111+
links.push({
112+
title: entry.title,
113+
description: entry.description,
114+
href: documentLink(entry.path, siteUrl, prerenderPaths),
115+
})
116+
}
117+
if (entry.children?.length) collect(entry.children)
118+
}
119+
}
120+
121+
if (item.page !== false && item.path && item.path !== '/') {
122+
links.push({
123+
title: item.title,
124+
description: item.description,
125+
href: documentLink(item.path, siteUrl, prerenderPaths),
126+
})
127+
}
128+
129+
if (item.children?.length) collect(item.children)
130+
131+
return links
132+
}
133+
134+
function documentLink(path: string, domain: string, prerenderPaths: Set<string>) {
135+
const href = joinURL(domain, rawUrlForPage(path))
136+
prerenderPaths.add(rawUrlForPage(path))
137+
return href
138+
}

‎server/routes/llms-full.txt.get.ts‎

Lines changed: 0 additions & 23 deletions
This file was deleted.

‎server/routes/llms.txt.get.ts‎

Lines changed: 0 additions & 52 deletions
This file was deleted.

0 commit comments

Comments
 (0)