Files
2026-07-13 13:22:34 +08:00

79 lines
2.1 KiB
TypeScript

import * as fs from 'fs';
import { XMLParser } from 'fast-xml-parser';
import fetch from 'node-fetch';
async function readSitemap(input: string): Promise<string> {
if (/^https?:\/\//.test(input)) {
const res = await fetch(input);
if (!res.ok) {
const responseBody = await res.text();
throw new Error(`Failed to fetch ${input}: ${res.status} ${res.statusText}. Response body: ${responseBody}`);
}
return await res.text();
} else {
return await fs.promises.readFile(input, 'utf8');
}
}
function normalizePath(url: string): string {
const idx = url.indexOf('/latest/');
return idx >= 0 ? url.slice(idx + 'latest/'.length) : url;
}
async function parseSitemap(input: string): Promise<Map<string, string>> {
const xml = await readSitemap(input);
const parser = new XMLParser();
const parsed = parser.parse(xml);
const urlset = parsed.urlset.url as { loc: string }[];
const urlMap = new Map<string, string>();
for (const { loc } of urlset) {
const normalized = normalizePath(loc);
urlMap.set(normalized, loc);
}
return urlMap;
}
function compareSitemaps(mapA: Map<string, string>, mapB: Map<string, string>) {
const onlyInA: string[] = [];
const onlyInB: string[] = [];
const inBoth: string[] = [];
for (const [url, _] of mapA) {
if (!mapB.has(url)) {
onlyInA.push(url);
} else {
inBoth.push(url);
}
}
for (const url of mapB.keys()) {
if (!mapA.has(url)) {
onlyInB.push(url);
}
}
return { onlyInA, onlyInB, inBoth };
}
(async () => {
const fileA = process.argv[2];
const fileB = process.argv[3];
if (!fileA || !fileB) {
console.error('Usage: tsx compare-sitemaps.ts <fileA|urlA> <fileB|urlB>');
process.exit(1);
}
const [mapA, mapB] = await Promise.all([parseSitemap(fileA), parseSitemap(fileB)]);
const { onlyInA, onlyInB, inBoth } = compareSitemaps(mapA, mapB);
console.log(`URLs in both: ${inBoth.length}`);
console.log(`Only in ${fileA}: ${onlyInA.length}`);
onlyInA.forEach((url) => console.log(` ${url}`));
console.log(`Only in ${fileB}: ${onlyInB.length}`);
onlyInB.forEach((url) => console.log(` ${url}`));
})();