@barodoc/plugin-llms-txt 7.0.0 → 8.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.
- package/dist/index.d.ts +26 -0
- package/dist/index.js +217 -0
- package/package.json +3 -3
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import * as _barodoc_core from '@barodoc/core';
|
|
2
|
+
|
|
3
|
+
interface LlmsTxtPluginOptions {
|
|
4
|
+
/**
|
|
5
|
+
* Custom site description for the llms.txt header.
|
|
6
|
+
* Defaults to the site name from config.
|
|
7
|
+
*/
|
|
8
|
+
description?: string;
|
|
9
|
+
/**
|
|
10
|
+
* Whether to generate llms-full.txt with complete page content.
|
|
11
|
+
* Defaults to true.
|
|
12
|
+
*/
|
|
13
|
+
full?: boolean;
|
|
14
|
+
/**
|
|
15
|
+
* Additional links to include in llms.txt
|
|
16
|
+
* e.g. GitHub repo, API reference, etc.
|
|
17
|
+
*/
|
|
18
|
+
links?: Array<{
|
|
19
|
+
title: string;
|
|
20
|
+
url: string;
|
|
21
|
+
description?: string;
|
|
22
|
+
}>;
|
|
23
|
+
}
|
|
24
|
+
declare const _default: (options: LlmsTxtPluginOptions) => _barodoc_core.BarodocPlugin;
|
|
25
|
+
|
|
26
|
+
export { type LlmsTxtPluginOptions, _default as default };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import { definePlugin } from "@barodoc/core";
|
|
5
|
+
function stripFrontmatter(content) {
|
|
6
|
+
if (!content.startsWith("---")) return content;
|
|
7
|
+
const end = content.indexOf("---", 3);
|
|
8
|
+
if (end === -1) return content;
|
|
9
|
+
return content.slice(end + 3).trimStart();
|
|
10
|
+
}
|
|
11
|
+
function extractFrontmatterField(content, field) {
|
|
12
|
+
if (!content.startsWith("---")) return "";
|
|
13
|
+
const end = content.indexOf("---", 3);
|
|
14
|
+
if (end === -1) return "";
|
|
15
|
+
const fm = content.slice(3, end);
|
|
16
|
+
const match = fm.match(new RegExp(`^${field}:\\s*(.+)$`, "m"));
|
|
17
|
+
return match ? match[1].trim().replace(/^["']|["']$/g, "") : "";
|
|
18
|
+
}
|
|
19
|
+
function extractTitle(content) {
|
|
20
|
+
const fmTitle = extractFrontmatterField(content, "title");
|
|
21
|
+
if (fmTitle) return fmTitle;
|
|
22
|
+
const body = stripFrontmatter(content);
|
|
23
|
+
const headingMatch = body.match(/^#\s+(.+)$/m);
|
|
24
|
+
return headingMatch ? headingMatch[1].trim() : "";
|
|
25
|
+
}
|
|
26
|
+
function cleanMarkdownForAI(md) {
|
|
27
|
+
const codeBlocks = [];
|
|
28
|
+
let cleaned = md.replace(/```[\s\S]*?```/g, (match) => {
|
|
29
|
+
codeBlocks.push(match);
|
|
30
|
+
return `__CODE_BLOCK_${codeBlocks.length - 1}__`;
|
|
31
|
+
});
|
|
32
|
+
cleaned = cleaned.replace(/<\/?(?:Callout|Steps|Step|Card|CardGroup|CodeGroup|CodeItem|ParamField|ParamFieldGroup|Tabs|Tab|Accordion|AccordionItem|Badge|Columns|Column|Expandable|Frame|FileTree|Tooltip|ResponseField|ApiReference|ApiEndpoint|ApiParams|ApiParam|ApiResponse|SimpleAccordion)[^>]*>/g, "").replace(/import\s+.*?from\s+["'].*?["'];?\s*\n?/g, "").replace(/^#{1,6}\s+/gm, "").replace(/\*\*(.+?)\*\*/g, "$1").replace(/\*(.+?)\*/g, "$1").replace(/`(.+?)`/g, "$1").replace(/\[(.+?)\]\(.+?\)/g, "$1").replace(/^[-*+]\s+/gm, "- ").replace(/^\s*>\s+/gm, "").replace(/\n{3,}/g, "\n\n");
|
|
33
|
+
for (let i = 0; i < codeBlocks.length; i++) {
|
|
34
|
+
cleaned = cleaned.replace(`__CODE_BLOCK_${i}__`, codeBlocks[i]);
|
|
35
|
+
}
|
|
36
|
+
return cleaned.trim();
|
|
37
|
+
}
|
|
38
|
+
function walkMdFiles(dir, base = dir) {
|
|
39
|
+
if (!fs.existsSync(dir)) return [];
|
|
40
|
+
const results = [];
|
|
41
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
42
|
+
const full = path.join(dir, entry.name);
|
|
43
|
+
if (entry.isDirectory()) {
|
|
44
|
+
results.push(...walkMdFiles(full, base));
|
|
45
|
+
} else if (/\.(md|mdx)$/.test(entry.name)) {
|
|
46
|
+
results.push(path.relative(base, full));
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return results;
|
|
50
|
+
}
|
|
51
|
+
function scanDocs(docsDir, locales, defaultLocale, navigation) {
|
|
52
|
+
const orderedSlugs = [];
|
|
53
|
+
for (const group of navigation) {
|
|
54
|
+
for (const slug of group.pages ?? []) {
|
|
55
|
+
if (!orderedSlugs.includes(slug)) {
|
|
56
|
+
orderedSlugs.push(slug);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
const seen = /* @__PURE__ */ new Set();
|
|
61
|
+
const pages = [];
|
|
62
|
+
function tryReadPage(slug, locale) {
|
|
63
|
+
const key = `${locale}:${slug}`;
|
|
64
|
+
if (seen.has(key)) return false;
|
|
65
|
+
for (const ext of [".mdx", ".md"]) {
|
|
66
|
+
const locPath = path.join(docsDir, locale, `${slug}${ext}`);
|
|
67
|
+
if (fs.existsSync(locPath)) {
|
|
68
|
+
const raw = fs.readFileSync(locPath, "utf-8");
|
|
69
|
+
pages.push({
|
|
70
|
+
slug,
|
|
71
|
+
locale,
|
|
72
|
+
title: extractTitle(raw) || slug,
|
|
73
|
+
description: extractFrontmatterField(raw, "description"),
|
|
74
|
+
content: stripFrontmatter(raw)
|
|
75
|
+
});
|
|
76
|
+
seen.add(key);
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
const flatPath = path.join(docsDir, `${slug}${ext}`);
|
|
80
|
+
if (fs.existsSync(flatPath)) {
|
|
81
|
+
const raw = fs.readFileSync(flatPath, "utf-8");
|
|
82
|
+
pages.push({
|
|
83
|
+
slug,
|
|
84
|
+
locale,
|
|
85
|
+
title: extractTitle(raw) || slug,
|
|
86
|
+
description: extractFrontmatterField(raw, "description"),
|
|
87
|
+
content: stripFrontmatter(raw)
|
|
88
|
+
});
|
|
89
|
+
seen.add(key);
|
|
90
|
+
return true;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
for (const slug of orderedSlugs) {
|
|
96
|
+
tryReadPage(slug, defaultLocale);
|
|
97
|
+
}
|
|
98
|
+
const allFiles = walkMdFiles(docsDir);
|
|
99
|
+
for (const relPath of allFiles) {
|
|
100
|
+
const slug = relPath.replace(/\.(mdx?)$/, "").replace(/\\/g, "/");
|
|
101
|
+
const parts = slug.split("/");
|
|
102
|
+
if (locales.includes(parts[0])) {
|
|
103
|
+
const locale = parts[0];
|
|
104
|
+
const innerSlug = parts.slice(1).join("/");
|
|
105
|
+
if (!seen.has(`${locale}:${innerSlug}`)) {
|
|
106
|
+
tryReadPage(innerSlug, locale);
|
|
107
|
+
}
|
|
108
|
+
} else {
|
|
109
|
+
if (!seen.has(`${defaultLocale}:${slug}`)) {
|
|
110
|
+
tryReadPage(slug, defaultLocale);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return pages;
|
|
115
|
+
}
|
|
116
|
+
function buildLlmsTxt(siteName, siteUrl, description, pages, defaultLocale, extraLinks) {
|
|
117
|
+
const lines = [];
|
|
118
|
+
lines.push(`# ${siteName}`);
|
|
119
|
+
lines.push("");
|
|
120
|
+
if (description) {
|
|
121
|
+
lines.push(`> ${description}`);
|
|
122
|
+
lines.push("");
|
|
123
|
+
}
|
|
124
|
+
const defaultPages = pages.filter((p) => p.locale === defaultLocale);
|
|
125
|
+
lines.push("## Documentation");
|
|
126
|
+
lines.push("");
|
|
127
|
+
for (const page of defaultPages) {
|
|
128
|
+
const url = siteUrl ? `${siteUrl.replace(/\/$/, "")}/${page.slug}` : `/${page.slug}`;
|
|
129
|
+
const desc = page.description ? `: ${page.description}` : "";
|
|
130
|
+
lines.push(`- [${page.title}](${url})${desc}`);
|
|
131
|
+
}
|
|
132
|
+
lines.push("");
|
|
133
|
+
if (extraLinks && extraLinks.length > 0) {
|
|
134
|
+
lines.push("## Links");
|
|
135
|
+
lines.push("");
|
|
136
|
+
for (const link of extraLinks) {
|
|
137
|
+
const desc = link.description ? `: ${link.description}` : "";
|
|
138
|
+
lines.push(`- [${link.title}](${link.url})${desc}`);
|
|
139
|
+
}
|
|
140
|
+
lines.push("");
|
|
141
|
+
}
|
|
142
|
+
return lines.join("\n");
|
|
143
|
+
}
|
|
144
|
+
function buildLlmsFullTxt(siteName, pages, defaultLocale) {
|
|
145
|
+
const lines = [];
|
|
146
|
+
lines.push(`# ${siteName} \u2014 Full Documentation`);
|
|
147
|
+
lines.push("");
|
|
148
|
+
lines.push(
|
|
149
|
+
`This file contains the complete documentation content for AI consumption.`
|
|
150
|
+
);
|
|
151
|
+
lines.push("");
|
|
152
|
+
lines.push("---");
|
|
153
|
+
lines.push("");
|
|
154
|
+
const defaultPages = pages.filter((p) => p.locale === defaultLocale);
|
|
155
|
+
for (const page of defaultPages) {
|
|
156
|
+
lines.push(`# ${page.title}`);
|
|
157
|
+
if (page.description) {
|
|
158
|
+
lines.push("");
|
|
159
|
+
lines.push(`_${page.description}_`);
|
|
160
|
+
}
|
|
161
|
+
lines.push("");
|
|
162
|
+
lines.push(cleanMarkdownForAI(page.content));
|
|
163
|
+
lines.push("");
|
|
164
|
+
lines.push("---");
|
|
165
|
+
lines.push("");
|
|
166
|
+
}
|
|
167
|
+
return lines.join("\n");
|
|
168
|
+
}
|
|
169
|
+
var index_default = definePlugin((options = {}) => {
|
|
170
|
+
const { description, full = true, links } = options;
|
|
171
|
+
return {
|
|
172
|
+
name: "@barodoc/plugin-llms-txt",
|
|
173
|
+
hooks: {
|
|
174
|
+
"build:done": async (buildContext, context) => {
|
|
175
|
+
const { outDir } = buildContext;
|
|
176
|
+
const { config, root } = context;
|
|
177
|
+
const siteName = config.name ?? "Documentation";
|
|
178
|
+
const siteUrl = config.site ?? "";
|
|
179
|
+
const siteDescription = description ?? "";
|
|
180
|
+
const defaultLocale = config.i18n?.defaultLocale ?? "en";
|
|
181
|
+
const locales = config.i18n?.locales ?? ["en"];
|
|
182
|
+
const navigation = config.navigation ?? [];
|
|
183
|
+
const customModeDir = path.join(root, "src", "content", "docs");
|
|
184
|
+
const docsDir = fs.existsSync(customModeDir) ? customModeDir : path.join(root, "docs");
|
|
185
|
+
const pages = scanDocs(docsDir, locales, defaultLocale, navigation);
|
|
186
|
+
if (pages.length === 0) {
|
|
187
|
+
console.log(
|
|
188
|
+
"[@barodoc/plugin-llms-txt] No pages found, skipping llms.txt generation."
|
|
189
|
+
);
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
const llmsTxt = buildLlmsTxt(
|
|
193
|
+
siteName,
|
|
194
|
+
siteUrl,
|
|
195
|
+
siteDescription,
|
|
196
|
+
pages,
|
|
197
|
+
defaultLocale,
|
|
198
|
+
links
|
|
199
|
+
);
|
|
200
|
+
fs.writeFileSync(path.join(outDir, "llms.txt"), llmsTxt, "utf-8");
|
|
201
|
+
console.log("[@barodoc/plugin-llms-txt] Generated llms.txt");
|
|
202
|
+
if (full) {
|
|
203
|
+
const llmsFullTxt = buildLlmsFullTxt(siteName, pages, defaultLocale);
|
|
204
|
+
fs.writeFileSync(
|
|
205
|
+
path.join(outDir, "llms-full.txt"),
|
|
206
|
+
llmsFullTxt,
|
|
207
|
+
"utf-8"
|
|
208
|
+
);
|
|
209
|
+
console.log("[@barodoc/plugin-llms-txt] Generated llms-full.txt");
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
};
|
|
214
|
+
});
|
|
215
|
+
export {
|
|
216
|
+
index_default as default
|
|
217
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@barodoc/plugin-llms-txt",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "8.0.0",
|
|
4
4
|
"description": "Generate llms.txt and llms-full.txt for AI consumption",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -15,13 +15,13 @@
|
|
|
15
15
|
"dist"
|
|
16
16
|
],
|
|
17
17
|
"peerDependencies": {
|
|
18
|
-
"@barodoc/core": "
|
|
18
|
+
"@barodoc/core": "8.0.0"
|
|
19
19
|
},
|
|
20
20
|
"devDependencies": {
|
|
21
21
|
"@types/node": "^22.0.0",
|
|
22
22
|
"tsup": "^8.3.0",
|
|
23
23
|
"typescript": "^5.7.0",
|
|
24
|
-
"@barodoc/core": "
|
|
24
|
+
"@barodoc/core": "8.0.0"
|
|
25
25
|
},
|
|
26
26
|
"license": "MIT",
|
|
27
27
|
"scripts": {
|