import fs from "node:fs"; import path from "node:path"; const root = process.cwd(); const config = fs.readFileSync(path.join(root, "zola.toml"), "utf8"); const postsDir = path.join(root, "content", "posts"); const output = path.join(root, "static", "llms.txt"); function tomlString(source, key) { const match = source.match( new RegExp(`^${key}\\s*=\\s*("(?:[^"\\\\]|\\\\.)*")`, "m"), ); return match ? JSON.parse(match[1]) : undefined; } function frontMatter(source) { return source.match(/^\+\+\+\s*\n([\s\S]*?)\n\+\+\+/)?.[1]; } function markdownFiles(directory) { return fs.readdirSync(directory, { withFileTypes: true }).flatMap((entry) => { const location = path.join(directory, entry.name); if (entry.isDirectory()) return markdownFiles(location); return entry.isFile() && entry.name.endsWith(".md") ? [location] : []; }); } function postUrl(file, metadata, baseUrl) { const relative = path.relative(postsDir, file).replaceAll(path.sep, "/"); const defaultSlug = relative.endsWith("/index.md") ? relative.slice(0, -"/index.md".length) : relative.slice(0, -".md".length); const slug = tomlString(metadata, "slug") ?? defaultSlug; return `${baseUrl}/posts/${slug}/`; } const baseUrl = tomlString(config, "base_url")?.replace(/\/$/, ""); const siteTitle = new URL(baseUrl).hostname; const summary = tomlString(config, "llms_description") ?? tomlString(config, "description"); const posts = markdownFiles(postsDir) .filter((file) => path.basename(file) !== "_index.md") .map((file) => { const metadata = frontMatter(fs.readFileSync(file, "utf8")); if (!metadata || /^draft\s*=\s*true$/m.test(metadata)) return null; const title = tomlString(metadata, "title"); const description = tomlString(metadata, "description"); const date = metadata.match(/^date\s*=\s*(\d{4}-\d{2}-\d{2})/m)?.[1]; if (!title || !date) return null; return { title, description, date, url: postUrl(file, metadata, baseUrl) }; }) .filter(Boolean) .sort((a, b) => b.date.localeCompare(a.date)); const lines = [ `# ${siteTitle}`, "", `> ${summary}`, "", "This site contains articles in English, often with commands, code samples, worked explanations, and material aimed at readers learning by doing.", "", "## Main pages", "", `- [Home](${baseUrl}/): Introduction and latest articles.`, `- [About](${baseUrl}/about/): About the author and contact information.`, `- [Posts](${baseUrl}/posts/): All published articles.`, `- [Tags](${baseUrl}/tags/): Articles grouped by topic.`, `- [Archive](${baseUrl}/archive/): Chronological article archive.`, "", "## Published articles", "", ...posts.map( ({ title, description, url }) => `- [${title}](${url})${description ? `: ${description.replace(/\s+/g, " ").trim()}` : ""}`, ), "", "## Feeds and discovery", "", `- [RSS feed](${baseUrl}/rss.xml): Full site feed in RSS format.`, `- [JSON feed](${baseUrl}/feed.json): Full site feed in JSON format.`, `- [Sitemap](${baseUrl}/sitemap.xml): Machine-readable index of public pages.`, "", ]; fs.writeFileSync(output, lines.join("\n")); console.log( `Generated ${path.relative(root, output)} with ${posts.length} published articles.`, );