From 43f4a5d8d13fc5f817c3111e58f01dd5b0018817 Mon Sep 17 00:00:00 2001 From: Juan Leal Date: Mon, 20 Jul 2026 19:33:31 -0400 Subject: [PATCH] fix: exclude dynamic templates from sitemap --- scripts/finalize-sitemap.ts | 12 +++++++++++- src/lib/sitemap.test.ts | 11 ++++++++++- src/lib/sitemap.ts | 4 ++++ vocs.config.ts | 2 ++ 4 files changed, 27 insertions(+), 2 deletions(-) diff --git a/scripts/finalize-sitemap.ts b/scripts/finalize-sitemap.ts index 7062a076..48bc0c38 100644 --- a/scripts/finalize-sitemap.ts +++ b/scripts/finalize-sitemap.ts @@ -4,7 +4,17 @@ import { finalizeSitemap } from '../src/lib/sitemap.ts' import { getBlogPostSlugs } from '../src/marketing/blogPlugin.ts' const sitemapPath = path.resolve('dist/public/sitemap.xml') -const sitemap = await fs.readFile(sitemapPath, 'utf-8') +let sitemap: string | undefined + +try { + sitemap = await fs.readFile(sitemapPath, 'utf-8') +} catch (error) { + const isMissingSitemap = error instanceof Error && 'code' in error && error.code === 'ENOENT' + if (!isMissingSitemap) throw error +} + +if (sitemap === undefined) process.exit(0) + const finalized = finalizeSitemap(sitemap, getBlogPostSlugs()) if (finalized !== sitemap) { diff --git a/src/lib/sitemap.test.ts b/src/lib/sitemap.test.ts index c50de00e..1f1a70f8 100644 --- a/src/lib/sitemap.test.ts +++ b/src/lib/sitemap.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from 'vitest' -import { finalizeSitemap } from './sitemap' +import { finalizeSitemap, shouldIncludeInSitemap } from './sitemap' const sitemap = ` @@ -46,3 +46,12 @@ describe('finalizeSitemap', () => { expect(result).toContain('https://tempo.xyz/developers/blog/t6') }) }) + +describe('shouldIncludeInSitemap', () => { + it('keeps concrete routes and excludes dynamic route templates', () => { + expect(shouldIncludeInSitemap('/blog/t7-network-upgrade')).toBe(true) + expect(shouldIncludeInSitemap('/docs/guide/payments')).toBe(true) + expect(shouldIncludeInSitemap('/blog/[slug]')).toBe(false) + expect(shouldIncludeInSitemap('/examples/[id]/details')).toBe(false) + }) +}) diff --git a/src/lib/sitemap.ts b/src/lib/sitemap.ts index 8740afcc..58b3e0e2 100644 --- a/src/lib/sitemap.ts +++ b/src/lib/sitemap.ts @@ -1,6 +1,10 @@ const TEMPLATE_URL_PATTERN = /\s*([^<]*\/\[[^\]]+\][^<]*)<\/loc>[\s\S]*?<\/url>\s*/g const LOCATION_PATTERN = /([^<]+)<\/loc>/g +export function shouldIncludeInSitemap(path: string): boolean { + return !/\/\[[^\]]+\](?:\/|$)/.test(path) +} + export function finalizeSitemap(sitemap: string, blogPostSlugs: readonly string[]): string { let blogBaseUrl: string | undefined diff --git a/vocs.config.ts b/vocs.config.ts index c31bcc2d..d0219ce7 100644 --- a/vocs.config.ts +++ b/vocs.config.ts @@ -1,6 +1,7 @@ import { Changelog, defineConfig, Embedding, McpSource, Reranker, Retriever } from 'vocs/config' import { docsRouteDestination, proxiedLegacyDocsRoutes } from './src/lib/docs-routing' import { createFeedbackAdapter } from './src/lib/feedback-adapter' +import { shouldIncludeInSitemap } from './src/lib/sitemap' // Only set baseUrl in production — Vocs injects a tag from this value, // which causes all links to resolve to the absolute URL on preview deployments. @@ -114,6 +115,7 @@ export default defineConfig({ }, }, sitemap: { + include: shouldIncludeInSitemap, lastmod: (path, { lastmod }) => { const pagePath = typeof path === 'string' ? path : '/' if (pagePath === '/docs' || pagePath.startsWith('/docs/')) return false