/** * Serializes sitemap entries to a clean, indented XML STRING — valid for crawlers * and readable in the browser as the native XML tree (indented, collapsible, * syntax-highlighted by the browser itself). * * NO XSLT stylesheet: browsers (Chrome et al.) are REMOVING XSLT support, so a * approach is a dead end — it shows a deprecation warning now * and will break soon. Instead we serve plain XML with the correct Content-Type; * the browser renders its built-in formatted XML view (the "code tree" look) with * no transformation needed. * * // app/sitemap.xml/route.ts * import { buildSitemapXml } from '@intecion/ipal-kit' * import { sitemap } from '@/lib/content' * export const dynamic = 'force-dynamic' * export async function GET() { * const entries = await sitemap() * const xml = buildSitemapXml(entries) * return new Response(xml, { * headers: { 'Content-Type': 'application/xml; charset=utf-8' }, * }) * } * * The indentation makes the raw XML pleasant to read; the browser's default XML * viewer adds the tree/fold UI. Crawlers read the XML as usual. * * NOTE: if you use this custom route, DON'T also keep app/sitemap.ts — pick one * (this route OR the Next MetadataRoute). Two sitemaps confuse crawlers. */ export function buildSitemapXml(entries) { const esc = (s)=>s.replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"').replace(/'/g, '''); const urls = entries.map((e)=>{ const parts = [ ` ${esc(e.url)}` ]; if (e.lastModified) { const iso = e.lastModified instanceof Date ? e.lastModified.toISOString() : String(e.lastModified); parts.push(` ${esc(iso)}`); } if (e.changeFrequency) { parts.push(` ${e.changeFrequency}`); } if (typeof e.priority === 'number') { parts.push(` ${e.priority}`); } const alternates = e.alternates?.languages; if (alternates) { for (const [lang, href] of Object.entries(alternates)){ if (typeof href === 'string') { parts.push(` `); } } } return ` \n${parts.join('\n')}\n `; }).join('\n'); return `\n` + `\n` + urls + `\n`; } //# sourceMappingURL=buildSitemapXml.js.map