Skip to content
◈ToolsThatRank
homerules_sitemapprivacyterms

Open source · Apache 2.0

rules_sitemap

Bazel rules that generate sitemap.xml, a sitemap index and robots.txt for a statically built site, and fail the build when the result would be invalid.

Source: github.com/theluckystrike/rules_sitemap. Module name theluckystrike_rules_sitemap. Pure Starlark: no toolchain, no host tools, no network access, and no transitive dependencies for consumers.

Why it exists

A sitemap is usually the last artefact in a static-site build and the least tested one. The failure mode is quiet. The file is served with HTTP 200, the crawler parses what it can, and the entries it rejects simply never appear in the index. Nothing in the build went red.

The three mistakes that cause it are all mechanical:

  • an unescaped & in a query string, which makes the document malformed XML;
  • a Sitemap: line in robots.txt still pointing at the previous origin, because that one directive has to be an absolute URL;
  • duplicate <loc> entries, or more than 50,000 of them in a single file.

These rules turn each of those into a build error instead of a silent deletion from the index.

Install

bazel_dep(name = "theluckystrike_rules_sitemap", version = "0.1.0")

Use

load("@theluckystrike_rules_sitemap//sitemap:defs.bzl", "robots_txt", "sitemap_xml")

PAGES = [
    "/",
    "/pricing/",
    "/docs/getting-started/",
]

sitemap_xml(
    name = "sitemap",
    base_url = "https://example.com",
    pages = PAGES,
    lastmod = "2026-08-09",
    changefreq = "weekly",
    out = "sitemap.xml",
)

robots_txt(
    name = "robots",
    base_url = "https://example.com",
    allow = ["/"],
    disallow = ["/admin/"],
    sitemaps = ["/sitemap.xml"],
    out = "robots.txt",
)

Page paths are root-relative and are joined onto base_url, so the same page list produces a correct sitemap for a staging origin and for production. Only base_url changes.

What is rejected at analysis time

InputError
base_url = "example.com"base_url must start with http:// or https://
pages = ["about/"]page paths must be root-relative and start with '/'
the same path listed twiceduplicate page path
more than 50,000 pages in one sitemapprotocol limit, with a pointer to sitemap_index
changefreq = "fortnightly"changefreq must be one of always, hourly, daily, weekly, monthly, yearly, never
priority = "1.5"priority must be between 0.0 and 1.0
lastmod = "09/08/2026"lastmod must be a W3C Datetime such as 2026-08-09
disallow = ["draft/"]disallow paths must be root-relative and start with '/'

Rules

RuleOutput
sitemap_xmlA sitemaps.org urlset document, with optional lastmod, changefreq and priority applied to every entry.
sitemap_indexA sitemaps.org sitemapindex document, for sites split across several sitemaps.
robots_txtA robots.txt whose Sitemap: references are made absolute from base_url.

Tests

bazel test //...

19 tests: Starlark unit tests for the rendering helpers, golden byte-comparison tests for each rule, and one analysis test per rejected input in the table above. examples/site is a standalone Bazel module that builds a three-page site and byte-compares both generated files against checked-in expected output.

Scope

The rules deliberately do not crawl a directory of HTML files to discover pages. Which URLs belong in a sitemap is a routing decision only the site generator knows — trailing slashes, pagination, canonical duplicates, noindex pages — so the page list is an explicit input. Generate it from whatever produces your routes and pass it in.

Licence

Apache License 2.0. Written and maintained by Michael Lip.

© 2026 toolsthatrank.com · home · rules_sitemap · privacy · terms