1
Files
2025/tools/lib/stats/articles.js
2025-11-28 01:47:10 +01:00

92 lines
2.5 KiB
JavaScript

const path = require("path");
const { DateTime } = require("luxon");
const { collectMarkdownFiles, collectSectionIndexDirs } = require("../content");
const { readFrontmatter } = require("../weather/frontmatter");
function parseDate(value) {
if (!value) return null;
if (value instanceof Date) {
return DateTime.fromJSDate(value);
}
if (typeof value === "string") {
let parsed = DateTime.fromISO(value);
if (!parsed.isValid) {
parsed = DateTime.fromRFC2822(value);
}
return parsed.isValid ? parsed : null;
}
return null;
}
function countWords(body) {
if (!body) return 0;
const cleaned = body
.replace(/```[\s\S]*?```/g, " ") // fenced code blocks
.replace(/`[^`]*`/g, " ") // inline code
.replace(/<[^>]+>/g, " "); // html tags
const words = cleaned.match(/[\p{L}\p{N}'-]+/gu);
return words ? words.length : 0;
}
async function loadArticles(contentDir) {
const files = await collectMarkdownFiles(contentDir);
const sectionDirs = await collectSectionIndexDirs(contentDir);
const rootDir = path.resolve(contentDir);
const articles = [];
function resolveSection(filePath) {
const absolute = path.resolve(filePath);
let current = path.dirname(absolute);
while (current.startsWith(rootDir)) {
if (sectionDirs.has(current)) {
return path.relative(rootDir, current).replace(/\\/g, "/") || ".";
}
const parent = path.dirname(current);
if (parent === current) break;
current = parent;
}
return null;
}
for (const file of files) {
const frontmatter = await readFrontmatter(file);
if (!frontmatter) continue;
const date = parseDate(frontmatter.doc.get("date"));
const title = frontmatter.doc.get("title") || path.basename(file, ".md");
const body = frontmatter.body.trim();
const wordCount = countWords(body);
const relativePath = path.relative(contentDir, file);
const section = resolveSection(file);
articles.push({
path: file,
relativePath,
title,
date,
body,
wordCount,
section,
frontmatter: frontmatter.doc.toJS ? frontmatter.doc.toJS() : frontmatter.doc.toJSON(),
});
}
return articles;
}
module.exports = {
collectMarkdownFiles,
countWords,
loadArticles,
parseDate,
};