90 lines
3.3 KiB
JavaScript
90 lines
3.3 KiB
JavaScript
import { buildLocalizedPath } from '../i18n/index.js';
|
|
/**
|
|
* Builds the body of /llms.txt — a Markdown file describing the site for AI
|
|
* agents / LLM crawlers, per the llmstxt.org convention. Mirrors buildRobots /
|
|
* buildSitemapEntries: the plugin already knows the site's name, description,
|
|
* and pages, so it can generate this automatically.
|
|
*
|
|
* Structure (llmstxt.org): H1 site name, a blockquote/summary, then a list of
|
|
* key pages as Markdown links with short descriptions. Agents read this to
|
|
* understand the site quickly without crawling everything.
|
|
*
|
|
* Wire it as a route that returns text/plain:
|
|
*
|
|
* // app/llms.txt/route.ts
|
|
* import { buildLlmsTxt } from '@intecion/ipal-kit'
|
|
* import { getCachedPayload } from '@/lib/content'
|
|
* import { i18nConfig } from '@/i18n.config'
|
|
* export const dynamic = 'force-dynamic'
|
|
* export async function GET() {
|
|
* const body = await buildLlmsTxt({
|
|
* payload: await getCachedPayload(),
|
|
* config: i18nConfig,
|
|
* baseUrl: process.env.NEXT_PUBLIC_SERVER_URL!,
|
|
* })
|
|
* return new Response(body, { headers: { 'Content-Type': 'text/plain; charset=utf-8' } })
|
|
* }
|
|
*
|
|
* Data comes from the panel (siteName, siteDescription, pages) — nothing
|
|
* hardcoded. Skips drafts and noindex pages (same as the sitemap).
|
|
*/ export async function buildLlmsTxt({ baseUrl, config, locale, pagesSlug = 'pages', payload, settingsSlug = 'site-settings' }) {
|
|
const loc = locale ?? config.defaultLocale;
|
|
const origin = baseUrl.replace(/\/$/, '');
|
|
const settings = await payload.findGlobal({
|
|
slug: settingsSlug,
|
|
depth: 0,
|
|
locale: loc
|
|
});
|
|
const name = settings.siteName?.trim() || 'Website';
|
|
const description = settings.siteDescription?.trim();
|
|
// NO where:{_status} filter — collections without drafts enabled don't
|
|
// register the _status field, and querying it throws
|
|
// "path cannot be queried: _status" (a real bug report). Draft filtering
|
|
// happens in memory below, which is safe for every collection. Same as
|
|
// buildSitemapEntries and generateStaticParams.
|
|
const result = await payload.find({
|
|
collection: pagesSlug,
|
|
depth: 0,
|
|
limit: 1000,
|
|
locale: loc
|
|
});
|
|
const lines = [
|
|
`# ${name}`,
|
|
''
|
|
];
|
|
if (description) {
|
|
lines.push(`> ${description}`, '');
|
|
}
|
|
const pageLinks = [];
|
|
for (const raw of result.docs){
|
|
if (raw._status && raw._status !== 'published') {
|
|
continue;
|
|
}
|
|
if (raw.meta?.noindex) {
|
|
continue;
|
|
}
|
|
const title = typeof raw.title === 'string' ? raw.title : undefined;
|
|
const slug = typeof raw.slug === 'string' ? raw.slug : undefined;
|
|
if (!title || !slug) {
|
|
continue;
|
|
}
|
|
const path = buildLocalizedPath({
|
|
config,
|
|
locale: loc,
|
|
slugs: {
|
|
[loc]: slug
|
|
}
|
|
});
|
|
if (!path) {
|
|
continue;
|
|
}
|
|
const pageDesc = typeof raw.meta?.description === 'string' ? ` — ${raw.meta.description}` : '';
|
|
pageLinks.push(`- [${title}](${origin}${path})${pageDesc}`);
|
|
}
|
|
if (pageLinks.length > 0) {
|
|
lines.push('## Strony', '', ...pageLinks, '');
|
|
}
|
|
return lines.join('\n');
|
|
}
|
|
|
|
//# sourceMappingURL=buildLlmsTxt.js.map
|