92 lines
2.5 KiB
JavaScript
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,
|
|
};
|