#!/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" }; } let existingWeather = null; if (frontmatter.doc.has("weather")) { existingWeather = frontmatter.doc.get("weather"); } if (!force && existingWeather) { 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) { if (weather) { finalWeather = weather; } } else if (existingWeather && typeof existingWeather === "object") { finalWeather = JSON.parse(JSON.stringify(existingWeather)); if (weather) { let providerName = null; if (weather.source && weather.source.length > 0) { providerName = weather.source[0]; } const added = mergeWeather(finalWeather, weather, providerName); if (!added && Object.keys(finalWeather).length === 0) { finalWeather = weather; } } } else if (weather) { finalWeather = weather; } frontmatter.doc.set("weather", finalWeather); await writeFrontmatter(filePath, frontmatter.doc, frontmatter.body); let status = "empty"; if (weather) { status = "updated"; } let sources = []; if (weather && weather.source) { sources = weather.source; } return { status, sources, }; } async function main() { const cliArgs = process.argv.slice(2); let force = false; if (cliArgs.includes("--force") || cliArgs.includes("-f")) { force = true; } 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; let sourcesLabel = "unknown source"; if (result.sources.length > 0) { sourcesLabel = result.sources.join(", "); } console.log(`✔ Added weather to ${relativePath} (${sourcesLabel})`); break; } case "empty": updated += 1; console.log(`• Added empty weather to ${relativePath}`); break; default: skipped += 1; } } 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); });