fix(www): include app routes in sitemap (#50277)

I added static App Router pages to the www sitemap, including the
homepage, pricing, and product pages. The generator previously scanned
only Pages Router and content files; it now strips route groups,
excludes dynamic segments, and emits these URLs without lastmod.

**Note:** The pre-existing Pages Router `/opt-out/[ref]` entry remains
outside this change.

## To test

Tested on the [www
preview](https://zone-www-dot-com-git-pamela-growth-1214-app-rou-1d4879-supabase.vercel.app/sitemap_www.xml):

- [x] Open `/sitemap_www.xml`: expect the homepage, `/pricing`, and
product routes once each, without route-group names or lastmod on those
entries.
- [x] Compare the sitemap's changelog URLs with `/changelog-rss.xml`:
expect every RSS item link to remain included, including text-slug
entries.
- [x] Open `/sitemap.xml`: expect the existing www and docs sitemap
links.

## Linear

- fixes GROWTH-1214


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- **New Features**
- Sitemap generation now includes static pages built with the Next.js
App Router.
  - Route groups are correctly omitted from generated URLs.
  - Dynamic App Router routes are excluded from the sitemap.

- **Bug Fixes**
- Improved sitemap coverage and URL accuracy for applications using both
App Router and Pages Router pages.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
Pamela Chia authored and GitHub committed 2026-09-16 16:54:42 +08:00
1 parent c52fca1340
commit 5e8e551e2c
2 files changed
+48

No files matched your search

+40
View File
@@ -189,6 +189,46 @@ describe('generate-sitemap lastmod', () => {
})
})
describe('generate-sitemap App Router pages', () => {
it.each(['app/page.tsx', 'app/(home)/page.tsx'])(
'includes only static page URLs without lastmod, with homepage at %s',
(homepage) => {
const dir = writeFixture({
[homepage]: '',
'app/(group)/example/page.tsx': '',
'app/(marketing)/guides/(topics)/start/page.tsx': '',
'app/blog/example/page.tsx': '',
'app/events/example/page.tsx': '',
'app/reference/index/page.tsx': '',
'app/things/[slug]/page.tsx': '',
'app/things/[slug]/details/page.tsx': '',
'app/(group)/[...slug]/page.tsx': '',
'app/optional/[[...slug]]/page.tsx': '',
'app/example/layout.tsx': '',
'app/api/route.ts': '',
'app/loading.tsx': '',
'pages/company.tsx': '',
})
const result = runGenerator(dir)
expect(result.status, result.stderr).toBe(0)
const entries = urlEntries(fs.readFileSync(path.join(dir, 'public/sitemap_www.xml'), 'utf-8'))
expect(entries.sort((a, b) => a.loc.localeCompare(b.loc))).toEqual(
[
'',
'/blog/example',
'/company',
'/evals',
'/events/example',
'/example',
'/guides/start',
'/reference/index',
].map((route) => ({ loc: `https://supabase.com${route}`, lastmod: undefined }))
)
},
SPAWN_TIMEOUT_MS
)
})
describe('frontmatter dates in sitemap and blog JSON-LD', () => {
it.each([
['2026-01-14T09:30:00', '2026-01-14'],
+8
View File
@@ -65,6 +65,7 @@ async function generate() {
'pages/*.tsx',
'pages/*.mdx',
'pages/**/*.tsx',
'app/**/page.tsx',
'_blog/*.mdx',
'_case-studies/*.mdx',
'_customers/*.mdx',
@@ -90,6 +91,13 @@ async function generate() {
// Generate URLs for static pages
const staticUrls = pages
.map((page) => {
if (page.startsWith('app/')) {
const segments = page.split('/').slice(1, -1)
if (segments.some((segment) => segment.includes('['))) return null
const path = segments.filter((segment) => !/^\(.*\)$/.test(segment)).join('/')
return urlEntry(`https://supabase.com${path ? `/${path}` : ''}`)
}
const path = page
.replace('.next/server/pages', '')
.replace(/^pages/, '')