Skip to content

Per-collection sitemaps with index and lastmod#431

Merged
ascorbic merged 4 commits intoemdash-cms:mainfrom
jdevalk:feat/per-collection-sitemaps
Apr 11, 2026
Merged

Per-collection sitemaps with index and lastmod#431
ascorbic merged 4 commits intoemdash-cms:mainfrom
jdevalk:feat/per-collection-sitemaps

Conversation

@jdevalk
Copy link
Copy Markdown
Contributor

@jdevalk jdevalk commented Apr 10, 2026

What does this PR do?

Refactors sitemap generation to produce a sitemap index (/sitemap.xml) with one child sitemap per SEO-enabled collection (/sitemap-{collection}.xml), each including <lastmod> values.

Per-collection sitemaps make it easier to spot template and content type issues in Google Search Console, since each collection shows as a separate sitemap.

Related discussion: #408

Type of change

  • Bug fix
  • Feature (requires approved Discussion)
  • Refactor (no behavior change)
  • Documentation
  • Performance improvement
  • Tests
  • Chore (dependencies, CI, tooling)

Checklist

  • I have read CONTRIBUTING.md
  • pnpm typecheck passes
  • pnpm --silent lint:json | jq '.diagnostics | length' returns 0
  • pnpm test passes (or targeted tests for my change)
  • pnpm format has been run
  • I have added/updated tests for my changes (if applicable)
  • I have added a changeset (if this PR changes a published package)
  • New features link to an approved Discussion: Where is the docs? #408

AI-generated code disclosure

  • This PR includes AI-generated code

Screenshots / test output

Changes:

  • /sitemap.xml now serves a <sitemapindex> with one <sitemap> entry per SEO-enabled collection, each with <lastmod>
  • New /sitemap-{collection}.xml routes serve per-collection <urlset> with <loc> and <lastmod> per URL
  • handleSitemapData returns both slug and id per entry, allowing correct interpolation of URL patterns using {slug}, {id}, or both
  • Dropped <changefreq> and <priority> (ignored by all major search engines)
  • Middleware regex for sitemap routes aligned with validateIdentifier() rules

Test coverage:

  • All 54 existing SEO tests updated and passing
  • New tests for urlPattern inclusion, lastmod on collections, collectionSlug filtering, and null slug handling

🤖 Generated with Claude Code

Split the single flat sitemap into a sitemap index at /sitemap.xml
with per-collection child sitemaps at /sitemap-{collection}.xml.
Each index entry includes <lastmod> from the most recently updated
content in that collection. Individual URL entries include <lastmod>.
Dropped <changefreq> and <priority> (ignored by Google).

The handler now returns data grouped by collection with url_pattern,
and accepts an optional collectionSlug filter for the per-collection
route to query only the requested collection.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings April 10, 2026 12:42
@changeset-bot
Copy link
Copy Markdown

changeset-bot bot commented Apr 10, 2026

🦋 Changeset detected

Latest commit: c196bf6

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 9 packages
Name Type
emdash Minor
@emdash-cms/cloudflare Patch
@emdash-cms/plugin-ai-moderation Major
@emdash-cms/plugin-atproto Patch
@emdash-cms/plugin-audit-log Patch
@emdash-cms/plugin-color Major
@emdash-cms/plugin-embeds Major
@emdash-cms/plugin-forms Major
@emdash-cms/plugin-webhook-notifier Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new bot commented Apr 10, 2026

Open in StackBlitz

@emdash-cms/admin

npm i https://pkg.pr.new/@emdash-cms/admin@431

@emdash-cms/auth

npm i https://pkg.pr.new/@emdash-cms/auth@431

@emdash-cms/blocks

npm i https://pkg.pr.new/@emdash-cms/blocks@431

@emdash-cms/cloudflare

npm i https://pkg.pr.new/@emdash-cms/cloudflare@431

emdash

npm i https://pkg.pr.new/emdash@431

create-emdash

npm i https://pkg.pr.new/create-emdash@431

@emdash-cms/gutenberg-to-portable-text

npm i https://pkg.pr.new/@emdash-cms/gutenberg-to-portable-text@431

@emdash-cms/x402

npm i https://pkg.pr.new/@emdash-cms/x402@431

@emdash-cms/plugin-ai-moderation

npm i https://pkg.pr.new/@emdash-cms/plugin-ai-moderation@431

@emdash-cms/plugin-atproto

npm i https://pkg.pr.new/@emdash-cms/plugin-atproto@431

@emdash-cms/plugin-audit-log

npm i https://pkg.pr.new/@emdash-cms/plugin-audit-log@431

@emdash-cms/plugin-color

npm i https://pkg.pr.new/@emdash-cms/plugin-color@431

@emdash-cms/plugin-embeds

npm i https://pkg.pr.new/@emdash-cms/plugin-embeds@431

@emdash-cms/plugin-forms

npm i https://pkg.pr.new/@emdash-cms/plugin-forms@431

@emdash-cms/plugin-webhook-notifier

npm i https://pkg.pr.new/@emdash-cms/plugin-webhook-notifier@431

commit: c196bf6

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates EmDash’s sitemap generation to produce a sitemap index (/sitemap.xml) with one child sitemap per SEO-enabled collection, and adds per-collection sitemap endpoints that include <lastmod> values.

Changes:

  • Refactors handleSitemapData to return sitemap entries grouped by collection (with urlPattern + per-collection lastmod) and adds optional collectionSlug filtering.
  • Updates /sitemap.xml to emit a <sitemapindex> pointing at /sitemap-{collection}.xml endpoints.
  • Adds a new per-collection sitemap route and wires it into Astro route injection + middleware public-runtime routing.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
packages/core/tests/integration/seo/seo.test.ts Updates existing SEO integration tests and adds coverage for urlPattern, collection lastmod, and collectionSlug filtering.
packages/core/src/astro/routes/sitemap.xml.ts Switches sitemap output from a single <urlset> to a <sitemapindex> with per-collection children and <lastmod>.
packages/core/src/astro/routes/sitemap-[collection].xml.ts Introduces the per-collection sitemap <urlset> endpoint that builds URLs using the collection url_pattern.
packages/core/src/astro/middleware.ts Treats /sitemap-{collection}.xml as a public runtime route (so runtime is initialized for sitemap requests).
packages/core/src/astro/integration/routes.ts Injects the new /sitemap-[collection].xml route into the Astro integration.
packages/core/src/api/handlers/seo.ts Refactors sitemap data aggregation to group by collection, include url_pattern, and compute collection-level lastmod.
packages/core/src/api/handlers/index.ts Re-exports the new SitemapCollectionData type.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@ascorbic
Copy link
Copy Markdown
Collaborator

Thanks! Could you address copilot's comments, and ask Claude to use the PR template

…hten regex

- SitemapContentEntry now returns slug and id separately instead of
  collapsing them into identifier, so URL patterns using {id} work
- Per-collection sitemap route interpolates both {slug} and {id}
  placeholders, matching interpolateUrlPattern behavior elsewhere
- Middleware regex for sitemap collection routes now matches
  validateIdentifier rules (no hyphens allowed in collection slugs)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@jdevalk
Copy link
Copy Markdown
Contributor Author

jdevalk commented Apr 11, 2026

Thanks! Could you address copilot's comments, and ask Claude to use the PR template

All done.

Copy link
Copy Markdown
Collaborator

@ascorbic ascorbic left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

@ascorbic ascorbic merged commit 7ee7d95 into emdash-cms:main Apr 11, 2026
28 checks passed
@emdashbot emdashbot bot mentioned this pull request Apr 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants