Sitemap Generator

Introduction

The sitemap generator (scripts/generate_sitemap.ts) produces a sitemap.xml file for search engine crawlers, along with a robots.txt that references it. It generates one <url> entry per (canonical page × active language), each carrying <xhtml:link rel="alternate"> entries for hreflang - mirroring the same hreflang block that the SSG script inlines into HTML pages.

bun sitemap

This should be run after bun ssg - it reads from the generated dist/ directory.

CLI Options

bun scripts/generate_sitemap.ts [options]
OptionDefaultDescription
--public <dir>./src/publicSource directory with page templates
--dist <dir>./distOutput directory; sitemap.xml is written here
--site-url <url>$SITE_URL (required)Absolute origin used for <loc> and hreflang URLs
--help-Print usage and exit

If --site-url is not provided and SITE_URL is not in the environment, the script exits with an error.

Output

The generator produces two files:

FileDescription
dist/sitemap.xmlXML Sitemap with hreflang alternates
dist/robots.txtRobots file pointing to the sitemap

sitemap.xml Structure

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="/sitemap.xsl"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
        xmlns:xhtml="http://www.w3.org/1999/xhtml">
  <url>
    <loc>https://example.com/</loc>
    <lastmod>2026-05-12</lastmod>
    <xhtml:link rel="alternate" hreflang="sl" href="https://example.com/"/>
    <xhtml:link rel="alternate" hreflang="en" href="https://example.com/en/"/>
    <xhtml:link rel="alternate" hreflang="x-default" href="https://example.com/"/>
  </url>
  <url>
    <loc>https://example.com/en/</loc>
    <lastmod>2026-05-12</lastmod>
    <xhtml:link rel="alternate" hreflang="sl" href="https://example.com/"/>
    <xhtml:link rel="alternate" hreflang="en" href="https://example.com/en/"/>
    <xhtml:link rel="alternate" hreflang="x-default" href="https://example.com/"/>
  </url>
</urlset>

Each canonical page generates one <url> per active language. All entries carry xhtml:link references to every language variant plus x-default (pointing to the default language).

robots.txt

User-agent: *
Allow: /

Sitemap: https://example.com/sitemap.xml

Per-Page Frontmatter

Pages can opt out of the sitemap or override metadata through frontmatter in either the base file or any language variant:

FrontmatterEffect
sitemap: falseExclude this canonical page from the sitemap
noindex: trueSame effect - exclude from sitemap
localize: falseIdentical content in every language - emit only the default-language URL (no per-language <url> entries or hreflang alternates)
lastmod: 2026-06-01Override the <lastmod> date (format: YYYY-MM-DD)

The <lastmod> date is resolved in this order:

  1. lastmod frontmatter, if set (highest priority)
  2. published_at frontmatter - or its alias date - for dated content like blog posts
  3. The file's modification time (mtime) - the most recent mtime across all language variants

This means a blog post dated with published_at reports that publication date as its <lastmod> even after the file is touched, without needing an explicit lastmod.

How It Works

  1. Loads translations via load_all_translations() to build the route map
  2. Collects page files via collect_page_files() - includes both .ree and .md files
  3. Builds route map via build_static_route_map() for localized URL resolution
  4. Reads frontmatter across all language variants for each canonical page
  5. Generates <url> entries - one per (canonical × active language) with hreflang alternates
  6. Writes sitemap.xml referencing the /sitemap.xsl stylesheet
  7. Writes robots.txt with a Sitemap: directive

Excluding Pages

To exclude a page from the sitemap, add to its frontmatter:

---
title: "Draft Page"
sitemap: false
---

Or using the alias:

---
title: "Private Notes"
noindex: true
---

Both have the same effect. The check is performed across all language variants - if any variant has sitemap: false or noindex: true, the canonical page is excluded.