diff --git a/.changeset/neat-tigers-float.md b/.changeset/neat-tigers-float.md new file mode 100644 index 000000000000..10326f6fef80 --- /dev/null +++ b/.changeset/neat-tigers-float.md @@ -0,0 +1,23 @@ +--- +'@astrojs/sitemap': minor +--- + +Customize the filenames of sitemap XML files generated by the `@astro/sitemap` integration by setting `filenameBase` in the integration configuration settings. This may be useful when deploying an Astro site at a path on a domain with preexisting sitemap files. + +Generated sitemap files will appear at `/sitemap-0.xml` and `/sitemap-index.xml` by default, which may conflict with preexisting files. Set `filenameBase` to a custom value to avoid that if so: + +```js + import { defineConfig } from 'astro/config'; + import sitemap from '@astrojs/sitemap'; + + export default defineConfig({ + site: 'https://example.com', + integrations: [ + sitemap({ + filenameBase: 'astronomy-sitemap', + }), + ], + }); +``` + +This will yield sitemap and index files as `https://example.com/astronomy-sitemap-0.xml` and `https://example.com/astronomy-sitemap-index.xml`. diff --git a/packages/integrations/sitemap/src/config-defaults.ts b/packages/integrations/sitemap/src/config-defaults.ts index 3d56483e196b..8d854c7a98e0 100644 --- a/packages/integrations/sitemap/src/config-defaults.ts +++ b/packages/integrations/sitemap/src/config-defaults.ts @@ -1,5 +1,6 @@ import type { SitemapOptions } from './index.js'; export const SITEMAP_CONFIG_DEFAULTS = { + filenameBase: 'sitemap', entryLimit: 45000, } satisfies SitemapOptions; diff --git a/packages/integrations/sitemap/src/index.ts b/packages/integrations/sitemap/src/index.ts index 9bbd39e4a71f..51b34abd5a4a 100644 --- a/packages/integrations/sitemap/src/index.ts +++ b/packages/integrations/sitemap/src/index.ts @@ -18,6 +18,7 @@ export type LinkItem = LinkItemBase; export type SitemapOptions = | { + filenameBase?: string; filter?(page: string): boolean; customPages?: string[]; @@ -46,7 +47,6 @@ function formatConfigErrorMessage(err: ZodError) { } const PKG_NAME = '@astrojs/sitemap'; -const OUTFILE = 'sitemap-index.xml'; const STATUS_CODE_PAGES = new Set(['404', '500']); const isStatusCodePage = (locales: string[]) => { @@ -88,8 +88,9 @@ const createPlugin = (options?: SitemapOptions): AstroIntegration => { const opts = validateOptions(config.site, options); - const { filter, customPages, serialize, entryLimit } = opts; + const { filenameBase, filter, customPages, serialize, entryLimit } = opts; + const outFile = `${filenameBase}-index.xml`; const finalSiteUrl = new URL(config.base, config.site); const shouldIgnoreStatus = isStatusCodePage(Object.keys(opts.i18n?.locales ?? {})); let pageUrls = pages @@ -144,7 +145,7 @@ const createPlugin = (options?: SitemapOptions): AstroIntegration => { } if (pageUrls.length === 0) { - logger.warn(`No pages found!\n\`${OUTFILE}\` not created.`); + logger.warn(`No pages found!\n\`${outFile}\` not created.`); return; } @@ -173,6 +174,7 @@ const createPlugin = (options?: SitemapOptions): AstroIntegration => { const xslURL = opts.xslURL ? new URL(opts.xslURL, finalSiteUrl).href : undefined; await writeSitemap( { + filenameBase: filenameBase, hostname: finalSiteUrl.href, destinationDir: destDir, publicBasePath: config.base, @@ -182,7 +184,7 @@ const createPlugin = (options?: SitemapOptions): AstroIntegration => { }, config, ); - logger.info(`\`${OUTFILE}\` created at \`${path.relative(process.cwd(), destDir)}\``); + logger.info(`\`${outFile}\` created at \`${path.relative(process.cwd(), destDir)}\``); } catch (err) { if (err instanceof ZodError) { logger.warn(formatConfigErrorMessage(err)); diff --git a/packages/integrations/sitemap/src/schema.ts b/packages/integrations/sitemap/src/schema.ts index ba5438452395..0ab9d672d49f 100644 --- a/packages/integrations/sitemap/src/schema.ts +++ b/packages/integrations/sitemap/src/schema.ts @@ -6,6 +6,7 @@ const localeKeySchema = z.string().min(1); export const SitemapOptionsSchema = z .object({ + filenameBase: z.string().optional().default(SITEMAP_CONFIG_DEFAULTS.filenameBase), filter: z.function().args(z.string()).returns(z.boolean()).optional(), customPages: z.string().url().array().optional(), canonicalURL: z.string().url().optional(), diff --git a/packages/integrations/sitemap/src/write-sitemap.ts b/packages/integrations/sitemap/src/write-sitemap.ts index 85efe4b263d5..939bd91be381 100644 --- a/packages/integrations/sitemap/src/write-sitemap.ts +++ b/packages/integrations/sitemap/src/write-sitemap.ts @@ -11,6 +11,7 @@ import type { AstroConfig } from 'astro'; import type { SitemapItem } from './index.js'; type WriteSitemapConfig = { + filenameBase: string; hostname: string; sitemapHostname?: string; sourceData: SitemapItem[]; @@ -23,6 +24,7 @@ type WriteSitemapConfig = { // adapted from sitemap.js/sitemap-simple export async function writeSitemap( { + filenameBase, hostname, sitemapHostname = hostname, sourceData, @@ -43,7 +45,7 @@ export async function writeSitemap( hostname, xslUrl, }); - const path = `./sitemap-${i}.xml`; + const path = `./${filenameBase}-${i}.xml`; const writePath = resolve(destinationDir, path); if (!publicBasePath.endsWith('/')) { publicBasePath += '/'; @@ -68,6 +70,6 @@ export async function writeSitemap( }); const src = Readable.from(sourceData); - const indexPath = resolve(destinationDir, `./sitemap-index.xml`); + const indexPath = resolve(destinationDir, `./${filenameBase}-index.xml`); return promisify(pipeline)(src, sitemapAndIndexStream, createWriteStream(indexPath)); } diff --git a/packages/integrations/sitemap/test/config.test.js b/packages/integrations/sitemap/test/config.test.js index fef9552dd9db..aaf66abc221e 100644 --- a/packages/integrations/sitemap/test/config.test.js +++ b/packages/integrations/sitemap/test/config.test.js @@ -80,4 +80,30 @@ describe('Config', () => { ); }); }); + + describe('Configuring the filename', () => { + before(async () => { + fixture = await loadFixture({ + root: './fixtures/static/', + integrations: [ + sitemap({ + filter: (page) => page === 'http://example.com/one/', + filenameBase: 'my-sitemap', + }), + ], + }); + await fixture.build(); + }); + + it('filenameBase: Sets the generated sitemap filename', async () => { + const data = await readXML(fixture.readFile('/my-sitemap-0.xml')); + const urls = data.urlset.url; + assert.equal(urls.length, 1); + + const indexData = await readXML(fixture.readFile('/my-sitemap-index.xml')); + const sitemapUrls = indexData.sitemapindex.sitemap; + assert.equal(sitemapUrls.length, 1); + assert.equal(sitemapUrls[0].loc[0], 'http://example.com/my-sitemap-0.xml'); + }); + }); });