1
Files
2025/tools/add_weather.js

143 lines
4.2 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" };
}
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);
});