#!/usr/bin/env node const path = require("path"); const { resolveMarkdownTargets } = require("./lib/content"); const { extractRawDate, readFrontmatter, writeFrontmatter } = require("./lib/weather/frontmatter"); const { resolveArticleDate } = require("./lib/weather/time"); const { fetchWeather, hasConfiguredProvider, mergeWeather } = require("./lib/weather/providers"); const { loadWeatherConfig } = require("./lib/weather/config"); const CONTENT_ROOT = path.resolve("content"); async function processFile(filePath, config, { force = false } = {}) { const frontmatter = await readFrontmatter(filePath); if (!frontmatter) { return { status: "no-frontmatter" }; } const existingWeather = frontmatter.doc.has("weather") ? frontmatter.doc.get("weather") : null; if (existingWeather && !force) { return { status: "already-set" }; } const dateValue = frontmatter.doc.get("date"); if (!dateValue) { return { status: "no-date" }; } const rawDate = extractRawDate(frontmatter.frontmatterText); const targetDate = resolveArticleDate(dateValue, rawDate, config); if (!targetDate) { return { status: "invalid-date" }; } const weather = await fetchWeather(targetDate, config); let finalWeather = {}; if (force) { finalWeather = weather || {}; } else if (existingWeather && typeof existingWeather === "object") { finalWeather = JSON.parse(JSON.stringify(existingWeather)); if (weather) { const added = mergeWeather(finalWeather, weather, weather.source?.[0]); if (!added && Object.keys(finalWeather).length === 0) { finalWeather = weather; } } } else { finalWeather = weather || {}; } frontmatter.doc.set("weather", finalWeather); await writeFrontmatter(filePath, frontmatter.doc, frontmatter.body); return { status: weather ? "updated" : "empty", sources: weather?.source || [], }; } async function main() { const cliArgs = process.argv.slice(2); const force = cliArgs.includes("--force") || cliArgs.includes("-f"); const pathArgs = cliArgs.filter((arg) => arg !== "--force" && arg !== "-f"); const config = loadWeatherConfig(); if (!hasConfiguredProvider(config)) { console.error("No weather provider configured. Update tools/config/config.json (weather.providers) before running this script."); process.exit(1); } const files = await resolveMarkdownTargets(pathArgs, { rootDir: CONTENT_ROOT }); if (files.length === 0) { console.log("No matching markdown files found."); return; } let updated = 0; let skipped = 0; for (const file of files) { const relativePath = path.relative(process.cwd(), file); try { const result = await processFile(file, config, { force }); switch (result.status) { case "updated": updated += 1; console.log(`✔ Added weather to ${relativePath} (${result.sources.join(", ") || "unknown source"})`); break; case "empty": updated += 1; console.log(`• Added empty weather to ${relativePath}`); break; default: skipped += 1; console.log(`↷ Skipped ${relativePath} (${result.status})`); } } catch (error) { skipped += 1; console.error(`✖ Failed ${relativePath}: ${error.message}`); } } console.log(`\nSummary: ${updated} updated, ${skipped} skipped.`); } main().catch((error) => { console.error(error); process.exit(1); });