#!/usr/bin/env node const fs = require("fs/promises"); const path = require("path"); const { extractRawDate, readFrontmatter, writeFrontmatter, } = require("./lib/weather/frontmatter"); const { parseFrontmatterDate, formatDateTime, getHugoTimeZone } = require("./lib/datetime"); const CONTENT_ROOT = path.join(process.cwd(), "content"); /** * Liste récursivement tous les fichiers Markdown d'un dossier. * @param {string} root Dossier racine à parcourir. * @returns {Promise} Chemins absolus des fichiers trouvés. */ async function listMarkdownFiles(root) { const entries = await fs.readdir(root, { withFileTypes: true }); const results = []; for (const entry of entries) { const fullPath = path.join(root, entry.name); if (entry.isDirectory()) { const nested = await listMarkdownFiles(fullPath); results.push(...nested); continue; } if (entry.isFile() && entry.name.endsWith(".md")) { results.push(fullPath); } } return results; } /** * Harmonise la date d'un fichier Markdown si nécessaire. * @param {string} filePath Chemin absolu du fichier. * @returns {Promise<"updated"|"unchanged"|"skipped"|"invalid">} Statut de traitement. */ async function normalizeFileDate(filePath) { const frontmatter = await readFrontmatter(filePath); if (!frontmatter) { return "skipped"; } const rawDate = extractRawDate(frontmatter.frontmatterText); const dateValue = frontmatter.doc.get("date"); if (!rawDate && (dateValue === undefined || dateValue === null)) { return "skipped"; } const sourceValue = rawDate !== null ? rawDate : dateValue; const parsed = parseFrontmatterDate(sourceValue); if (!parsed) { return "invalid"; } let hasTime = false; if (typeof rawDate === "string") { const timeMatch = rawDate.match(/[T ](\d{2}):(\d{2})(?::(\d{2}))?/); if (timeMatch) { const hour = Number(timeMatch[1]); const minute = Number(timeMatch[2]); const second = Number(timeMatch[3] || "0"); hasTime = !(hour === 0 && minute === 0 && second === 0); } } const normalized = hasTime ? parsed.set({ millisecond: 0 }) : parsed.set({ hour: 12, minute: 0, second: 0, millisecond: 0 }); const formatted = formatDateTime(normalized); const current = typeof dateValue === "string" ? dateValue.trim() : rawDate; const quotedFormatted = `"${formatted}"`; const currentComparable = typeof current === "string" ? current.trim() : ""; if (currentComparable === formatted || currentComparable === quotedFormatted) { return "unchanged"; } frontmatter.doc.set("date", formatted); await writeFrontmatter(filePath, frontmatter.doc, frontmatter.body); const rewritten = await fs.readFile(filePath, "utf8"); const normalizedContent = rewritten.replace(/^date:\s*.+$/m, `date: ${quotedFormatted}`); if (rewritten !== normalizedContent) { await fs.writeFile(filePath, normalizedContent, "utf8"); } return "updated"; } /** * Point d'entrée du script. */ async function main() { const timezone = getHugoTimeZone(); console.log(`Fuseau horaire Hugo : ${timezone}`); const files = await listMarkdownFiles(CONTENT_ROOT); let updated = 0; let unchanged = 0; let skipped = 0; let invalid = 0; for (const file of files) { const status = await normalizeFileDate(file); if (status === "updated") updated += 1; else if (status === "unchanged") unchanged += 1; else if (status === "invalid") { invalid += 1; const relative = path.relative(process.cwd(), file); console.warn(`Date invalide : ${relative}`); } else { skipped += 1; } } console.log( `Terminé. ${updated} mis à jour, ${unchanged} inchangés, ${skipped} ignorés, ${invalid} invalides.` ); } main();