2022-03-18 22:35:45 +00:00
|
|
|
import type { AstroConfig, AstroIntegration } from 'astro';
|
2022-06-06 16:49:53 +00:00
|
|
|
import fs from 'node:fs';
|
2022-03-18 22:35:45 +00:00
|
|
|
const STATUS_CODE_PAGE_REGEXP = /\/[0-9]{3}\/?$/;
|
|
|
|
|
2022-04-02 18:29:59 +00:00
|
|
|
type SitemapOptions =
|
|
|
|
| {
|
|
|
|
/**
|
|
|
|
* All pages are included in your sitemap by default.
|
|
|
|
* With this config option, you can filter included pages by URL.
|
|
|
|
*
|
|
|
|
* The `page` function parameter is the full URL of your rendered page, including your `site` domain.
|
|
|
|
* Return `true` to include a page in your sitemap, and `false` to remove it.
|
|
|
|
*
|
|
|
|
* ```js
|
|
|
|
* filter: (page) => page !== 'http://example.com/secret-page'
|
|
|
|
* ```
|
|
|
|
*/
|
2022-06-10 18:16:08 +00:00
|
|
|
filter?(page: string): boolean;
|
2022-04-02 18:29:59 +00:00
|
|
|
|
2022-05-12 20:19:58 +00:00
|
|
|
/**
|
|
|
|
* If you have any URL, not rendered by Astro, that you want to include in your sitemap,
|
|
|
|
* this config option will help you to include your array of custom pages in your sitemap.
|
|
|
|
*
|
|
|
|
* ```js
|
|
|
|
* customPages: ['http://example.com/custom-page', 'http://example.com/custom-page2']
|
|
|
|
* ```
|
|
|
|
*/
|
|
|
|
customPages?: Array<string>;
|
|
|
|
|
2022-04-02 18:29:59 +00:00
|
|
|
/**
|
|
|
|
* If present, we use the `site` config option as the base for all sitemap URLs
|
|
|
|
* Use `canonicalURL` to override this
|
|
|
|
*/
|
|
|
|
canonicalURL?: string;
|
|
|
|
}
|
|
|
|
| undefined;
|
|
|
|
|
2022-03-18 22:35:45 +00:00
|
|
|
/** Construct sitemap.xml given a set of URLs */
|
|
|
|
function generateSitemap(pages: string[]) {
|
|
|
|
// TODO: find way to respect <link rel="canonical"> URLs here
|
|
|
|
const urls = [...pages].filter((url) => !STATUS_CODE_PAGE_REGEXP.test(url));
|
|
|
|
urls.sort((a, b) => a.localeCompare(b, 'en', { numeric: true })); // sort alphabetically so sitemap is same each time
|
|
|
|
let sitemap = `<?xml version="1.0" encoding="UTF-8"?><urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">`;
|
|
|
|
for (const url of urls) {
|
|
|
|
sitemap += `<url><loc>${url}</loc></url>`;
|
|
|
|
}
|
|
|
|
sitemap += `</urlset>\n`;
|
|
|
|
return sitemap;
|
|
|
|
}
|
|
|
|
|
2022-04-02 18:29:59 +00:00
|
|
|
export default function createPlugin({
|
|
|
|
filter,
|
2022-05-12 20:19:58 +00:00
|
|
|
customPages,
|
2022-04-02 18:29:59 +00:00
|
|
|
canonicalURL,
|
|
|
|
}: SitemapOptions = {}): AstroIntegration {
|
2022-03-18 22:35:45 +00:00
|
|
|
let config: AstroConfig;
|
|
|
|
return {
|
|
|
|
name: '@astrojs/sitemap',
|
|
|
|
hooks: {
|
|
|
|
'astro:config:done': async ({ config: _config }) => {
|
|
|
|
config = _config;
|
|
|
|
},
|
|
|
|
'astro:build:done': async ({ pages, dir }) => {
|
2022-06-08 15:07:12 +00:00
|
|
|
let finalSiteUrl: URL;
|
|
|
|
if (canonicalURL) {
|
|
|
|
finalSiteUrl = new URL(canonicalURL);
|
|
|
|
finalSiteUrl.pathname += finalSiteUrl.pathname.endsWith('/') ? '' : '/'; // normalizes the final url since it's provided by user
|
|
|
|
} else if (config.site) {
|
|
|
|
finalSiteUrl = new URL(config.base, config.site);
|
|
|
|
} else {
|
2022-04-02 18:29:59 +00:00
|
|
|
console.warn(
|
|
|
|
'The Sitemap integration requires either the `site` astro.config option or `canonicalURL` integration option. Skipping.'
|
|
|
|
);
|
2022-03-18 22:35:45 +00:00
|
|
|
return;
|
|
|
|
}
|
2022-06-08 15:07:12 +00:00
|
|
|
let pageUrls = pages.map((p) => {
|
2022-06-08 15:09:17 +00:00
|
|
|
const path = finalSiteUrl.pathname + p.pathname;
|
|
|
|
return new URL(path, finalSiteUrl).href;
|
2022-06-08 15:07:12 +00:00
|
|
|
});
|
2022-04-02 18:29:59 +00:00
|
|
|
if (filter) {
|
|
|
|
pageUrls = pageUrls.filter((page: string) => filter(page));
|
|
|
|
}
|
2022-05-12 20:19:58 +00:00
|
|
|
if (customPages) {
|
|
|
|
pageUrls = [...pageUrls, ...customPages];
|
|
|
|
}
|
2022-03-18 22:35:45 +00:00
|
|
|
const sitemapContent = generateSitemap(pageUrls);
|
|
|
|
fs.writeFileSync(new URL('sitemap.xml', dir), sitemapContent);
|
|
|
|
},
|
|
|
|
},
|
|
|
|
};
|
|
|
|
}
|