Skip to content

Commit 955c993

Browse files
authored
feat(docs): split llms.txt into spec-compliant index and full dump (#2283)
1 parent 09d0af9 commit 955c993

2 files changed

Lines changed: 64 additions & 0 deletions

File tree

packages/stacks-docs/.eleventy.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,12 @@ module.exports = function(eleventyConfig) {
1818
eleventyConfig.addPlugin(iconPlugin);
1919
eleventyConfig.addPlugin(headerPlugin);
2020
eleventyConfig.addPlugin(tipPlugin);
21+
// Produces /llms-full.txt — a content dump for tools that want the full
22+
// markdown of every page in one file. The spec-compliant index lives at
23+
// /llms.txt and is rendered by llms.11ty.js.
2124
eleventyConfig.addPlugin(llmsTxtPlugin, {
2225
siteUrl: 'https://v2.stackoverflow.design',
26+
outputPath: 'llms-full.txt',
2327
collections: ['base', 'components', 'develop', 'foundation'],
2428
additionalMetadata: ['description'],
2529
normalizeWhitespace: true,

packages/stacks-docs/llms.11ty.js

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// Renders the spec-compliant llms.txt index at /llms.txt
2+
// (see https://llmstxt.org). The full content dump lives at /llms-full.txt,
3+
// generated by eleventy-plugin-llms-txt — see .eleventy.js.
4+
5+
const SECTIONS = [
6+
{ tag: "base", heading: "Base utilities" },
7+
{ tag: "components", heading: "Components" },
8+
{ tag: "develop", heading: "Develop" },
9+
{ tag: "foundation", heading: "Foundation" },
10+
];
11+
12+
// Descriptions may contain inline HTML for the rendered page; strip it for
13+
// llms.txt. Loop until stable so unclosed/nested-looking tags can't reintroduce
14+
// the pattern (CodeQL js/incomplete-multi-character-sanitization).
15+
function stripHtml(input) {
16+
let previous;
17+
let output = input;
18+
do {
19+
previous = output;
20+
output = output.replace(/<[^>]+>/g, "");
21+
} while (output !== previous);
22+
return output;
23+
}
24+
25+
function pageLink(page, siteUrl) {
26+
const title = page.data.title;
27+
const description = stripHtml(page.data.description || "")
28+
.replace(/\s+/g, " ")
29+
.trim();
30+
const suffix = description ? `: ${description}` : "";
31+
return `- [${title}](${siteUrl}${page.url})${suffix}`;
32+
}
33+
34+
module.exports = class {
35+
data() {
36+
return {
37+
permalink: "/llms.txt",
38+
eleventyExcludeFromCollections: true,
39+
};
40+
}
41+
42+
render({ collections, site }) {
43+
const sections = SECTIONS.map(({ tag, heading }) => {
44+
const pages = (collections[tag] || [])
45+
.slice()
46+
.sort((a, b) =>
47+
(a.data.title || "").localeCompare(b.data.title || "")
48+
);
49+
const lines = pages.map((p) => pageLink(p, site.url));
50+
return `## ${heading}\n\n${lines.join("\n")}`;
51+
});
52+
53+
return `# ${site.title}
54+
55+
> ${site.description}
56+
57+
${sections.join("\n\n")}
58+
`;
59+
}
60+
};

0 commit comments

Comments
 (0)