A sitemap aids discovery, not ranking. Google "ignores <priority> and <changefreq> values" and trusts <lastmod> only when "consistently and verifiably" accurate. Use absolute canonical URLs, at most 50,000 per file (50 MB), and keep the file at the root, because a sitemap covers only its own directory unless submitted in Search Console 1 (Sitemaps and robots.txt). The script escapes & as &.
import { writeFileSync } from 'node:fs';
const site = 'https://example.com';
const pages = [
{ path: '/', updated: '2026-09-01' },
{ path: '/product?id=45&lang=en', updated: '2026-07-30' },
];
const esc = (s) => s.replace(/&/g, '&').replace(/</g, '<');
const urls = pages.map((p) => ` <url><loc>${esc(site + p.path)}</loc>`
+ `<lastmod>${p.updated}</lastmod></url>`);
const xml = ['<?xml version="1.0" encoding="UTF-8"?>',
'<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">', ...urls, '</urlset>'];
writeFileSync('sitemap.xml', xml.join('\n') + '\n');
console.log(xml.join('\n'));Output
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url><loc>https://example.com/</loc><lastmod>2026-09-01</lastmod></url>
<url><loc>https://example.com/product?id=45&lang=en</loc><lastmod>2026-07-30</lastmod></u
rl>
</urlset>Take lastmod from real content changes (a CMS field or git log), never from build time.