diff --git a/.changeset/cool-ravens-occur.md b/.changeset/cool-ravens-occur.md new file mode 100644 index 0000000000..fb934ae06f --- /dev/null +++ b/.changeset/cool-ravens-occur.md @@ -0,0 +1,18 @@ +--- +'astro': patch +--- + +Add user-configurable `sitemapFilter` option. + +This option can be used to ensure certain pages are excluded from your final sitemap. + +```ts +// astro.config.ts +import type { AstroUserConfig } from 'astro' + +const config: AstroUserConfig = { + sitemap: true, + sitemapFilter: (page: string) => !page.includes('secret-page') +} +export default config +``` diff --git a/packages/astro/src/@types/astro.ts b/packages/astro/src/@types/astro.ts index 975afd1923..de2449104c 100644 --- a/packages/astro/src/@types/astro.ts +++ b/packages/astro/src/@types/astro.ts @@ -226,6 +226,25 @@ export interface AstroUserConfig { */ sitemap?: boolean; + /** + * @docs + * @name buildOptions.sitemapFilter + * @type {undefined|((page: string) => boolean)} + * @default `undefined` + * @description + * Customize sitemap generation for your build by excluding certain pages. + * + * ```js + * { + * buildOptions: { + * sitemap: true + * sitemapFilter: (page) => !page.includes('secret-page') + * } + * } + * ``` + */ + sitemapFilter?: (page: string) => boolean + /** * @docs * @name buildOptions.pageUrlFormat diff --git a/packages/astro/src/core/build/index.ts b/packages/astro/src/core/build/index.ts index 0e5a50f223..816abd8bad 100644 --- a/packages/astro/src/core/build/index.ts +++ b/packages/astro/src/core/build/index.ts @@ -149,7 +149,8 @@ class AstroBuilder { // Build your final sitemap. if (this.config.buildOptions.sitemap && this.config.buildOptions.site) { timer.sitemapStart = performance.now(); - const sitemap = generateSitemap(pageNames.map((pageName) => new URL(pageName, this.config.buildOptions.site).href)); + const sitemapFilter = this.config.buildOptions.sitemapFilter ? (this.config.buildOptions.sitemapFilter as (page: string) => boolean) : undefined; + const sitemap = generateSitemap(pageNames.map((pageName) => new URL(pageName, this.config.buildOptions.site).href), sitemapFilter); const sitemapPath = new URL('./sitemap.xml', this.config.dist); await fs.promises.mkdir(new URL('./', sitemapPath), { recursive: true }); await fs.promises.writeFile(sitemapPath, sitemap, 'utf8'); diff --git a/packages/astro/src/core/config.ts b/packages/astro/src/core/config.ts index 5f5ca042e1..e20bc4132d 100644 --- a/packages/astro/src/core/config.ts +++ b/packages/astro/src/core/config.ts @@ -50,6 +50,7 @@ export const AstroConfigSchema = z.object({ .string() .optional() .transform((val) => (val ? addTrailingSlash(val) : val)), + sitemapFilter: z.function().optional(), sitemap: z.boolean().optional().default(true), pageUrlFormat: z .union([z.literal('file'), z.literal('directory')]) diff --git a/packages/astro/src/core/render/sitemap.ts b/packages/astro/src/core/render/sitemap.ts index 4f695253d4..2956d72ace 100644 --- a/packages/astro/src/core/render/sitemap.ts +++ b/packages/astro/src/core/render/sitemap.ts @@ -1,14 +1,19 @@ + const STATUS_CODE_PAGE_REGEXP = /\/[0-9]{3}\/?$/; /** Construct sitemap.xml given a set of URLs */ -export function generateSitemap(pages: string[]): string { +export function generateSitemap(pages: string[], filter?: (page: string) => boolean): string { // TODO: find way to respect URLs here - // TODO: find way to exclude pages from sitemap // copy just in case original copy is needed // make sure that 404 page is excluded // also works for other error pages - const urls = [...pages].filter((url) => !STATUS_CODE_PAGE_REGEXP.test(url)); + let urls = [...pages].filter((url) => !STATUS_CODE_PAGE_REGEXP.test(url)); + + if (filter) { + urls = urls.filter(url => filter(url)); + } + urls.sort((a, b) => a.localeCompare(b, 'en', { numeric: true })); // sort alphabetically so sitemap is same each time let sitemap = ``; for (const url of urls) {