vuepress-plugin-llms
Version:
📜 VuePress plugin for generating documentation friendly to Large Language Models (LLMs) | 📜 VuePress 插件,用于生成对大语言模型(LLMs)友好的文档。
134 lines (133 loc) • 5.17 kB
JavaScript
import fs from 'node:fs/promises';
import path from 'node:path';
import matter from 'gray-matter';
import pc from 'picocolors';
import { remark } from 'remark';
import remarkFrontmatter from 'remark-frontmatter';
import { expandTemplate, generateMetadata, normalizePath } from './utils.js';
import log from './logger.js';
/**
* Generate the contents of the `llms.txt` file, which contains a table of contents
* with links to all sections of the documentation.
*/
/**
* 生成 `llms.txt` 文件的内容,其中包含指向文档所有部分的链接的目录。
*/
export async function generateLLMsTxt(preparedFiles, options) {
log.info('Generating toc...');
const { indexMd, LLMsTxtTemplate, templateVariables, siteConfig, domain, linksExtension, cleanUrls, srcDir, } = options;
let variables = {
title: siteConfig.title,
description: siteConfig.description,
toc: '',
...templateVariables,
};
// Try to read index.md if it exists
// 如果 index.md 存在,尝试读取它
try {
if (indexMd) {
log.info(`Reading index.md from: ${pc.cyan(indexMd)}`);
const indexContent = await fs.readFile(indexMd, 'utf-8');
const indexMdData = matter(indexContent);
if (indexMdData.data.title && !templateVariables.title) {
variables.title = indexMdData.data.title;
}
if (indexMdData.data.description && !templateVariables.description) {
variables.description = indexMdData.data.description;
}
}
}
catch (error) {
log.warn(`Failed to read index.md: ${error.message}`);
}
// Generate TOC if requested
// 如果请求,生成目录
if (!(typeof variables.toc === 'boolean' && variables.toc === false)) {
log.info('Generating TOC from prepared files');
variables.toc = await generateTOC(preparedFiles, {
srcDir,
domain,
linksExtension,
cleanUrls,
});
}
// Expand template with variables
// 使用变量展开模板
return expandTemplate(LLMsTxtTemplate, variables);
}
/**
* Generate the contents of the `llms-full.txt` file, which contains the entire
* content of all documentation embedded directly.
*/
/**
* 生成 `llms-full.txt` 文件的内容,其中直接嵌入了所有文档的全部内容。
*/
export async function generateLLMsFullTxt(preparedFiles, options) {
const { srcDir, domain, linksExtension, cleanUrls } = options;
log.info('Generating full content file');
// Process each file by adding a divider and metadata
// 通过添加分隔符和元数据处理每个文件
const sections = await Promise.all(preparedFiles.map(async (file) => {
// For each file, we'll create a section with metadata and content
// 对于每个文件,我们将创建一个包含元数据和内容的部分
const relativePath = path.relative(srcDir, file.path);
const metadata = generateMetadata({
title: file.title,
relativePath,
domain,
linksExtension,
cleanUrls,
});
let contentToProcess = file.file.content;
// Convert markdown to plain text and strip HTML tags
// 将 markdown 转换为纯文本并去除 HTML 标签
try {
const processedContent = await remark()
.use(remarkFrontmatter)
.process(contentToProcess);
// Strip HTML tags from the processed content
// 从处理后的内容中去除 HTML 标签
let htmlContent = String(processedContent);
contentToProcess = htmlContent.replace(/<[^>]*>/g, '');
}
catch (error) {
log.warn(`Failed to process HTML in ${file.path}: ${error.message}`);
}
return `${metadata}\n\n${contentToProcess}`;
}));
// Combine all sections with dividers
// 用分隔符组合所有部分
return sections.join('\n\n---\n\n');
}
/**
* Generate a table of contents from the prepared files.
*/
/**
* 从准备好的文件生成目录。
*/
export async function generateTOC(preparedFiles, options) {
const { srcDir, domain, linksExtension, cleanUrls } = options;
return preparedFiles
.map((file) => {
const relativePath = path.relative(srcDir, file.path);
const normalizedPath = normalizePath(relativePath, {
domain,
linksExtension,
cleanUrls,
});
// Get first sentence from content if available
// 如果可用,从内容中获取第一句话
let description = '';
const firstPara = file.file.content.split('\n\n')[0];
if (firstPara) {
// Extract text of the first sentence
// 提取第一句话的文本
const match = firstPara.match(/^[^.!?]+[.!?]/);
if (match) {
description = match[0].trim();
}
}
return `- [${file.title}](${normalizedPath})${description ? `: ${description}` : ''}`;
})
.join('\n');
}