const XML_ESCAPE_PATTERN = /[&<>"']/g; const XML_ESCAPES: Record = { '"': '"', '&': '&', "'": ''', '<': '<', '>': '>', }; /** A page to list in the sitemap. */ export interface SitemapPage { url: string; } /** * Builds a sitemaps.org 0.9 protocol document. Every URL is absolute and * trailing-slashed, except the homepage, matching the URL the host serves * with a 200 instead of a redirect. */ export function buildSitemap(origin: string, pages: Array): string { const entries = pages.map((page) => buildUrlEntry(origin, page)).join(''); return ( '\n' + '\n' + entries + '\n' ); } function buildUrlEntry(origin: string, page: SitemapPage): string { const loc = escapeXml(`${origin}${sitemapPath(page.url)}`); return ` \n ${loc}\n \n`; } function sitemapPath(url: string): string { if (url === '/') return '/'; return url.endsWith('/') ? url : `${url}/`; } function escapeXml(value: string): string { return value.replace(XML_ESCAPE_PATTERN, (char) => XML_ESCAPES[char] ?? char); }