1
Files
2025/tools/add_weather.js

117 lines
3.7 KiB
JavaScript

#!/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);
});