@analogjs/vite-plugin-nitro
Version:
A Vite plugin for adding a nitro API server
127 lines • 4.72 kB
JavaScript
import { writeFileSync } from 'node:fs';
import { create } from 'xmlbuilder2';
import { resolve } from 'node:path';
export async function buildSitemap(config, sitemapConfig, routes, outputDir, routeSitemaps, i18nOptions) {
const routeList = await optionHasRoutes(routes);
if (routeList.length) {
const slash = checkSlash(sitemapConfig.host || '');
const sitemapData = routeList.map((page) => {
const url = `${slash}${page.replace(/^\/+/g, '')}`;
const config = routeSitemaps[url];
const props = typeof config === 'object' ? config : config?.();
return {
page: `${sitemapConfig.host}${url}`,
lastMod: props?.lastmod ?? new Date().toISOString().split('T')[0],
changefreq: props?.changefreq,
priority: props?.priority,
};
});
const hasI18n = i18nOptions && i18nOptions.locales.length > 1;
const sitemap = createXml('urlset', hasI18n);
for (const item of sitemapData) {
const page = sitemap.ele('url');
page.ele('loc').txt(item.page);
page.ele('lastmod').txt(item.lastMod);
if (item.changefreq) {
page.ele('changefreq').txt(item.changefreq);
}
if (item.priority) {
page.ele('priority').txt(item.priority);
}
// Add hreflang alternate links for each locale
if (hasI18n) {
const alternates = getHreflangAlternates(item.page, sitemapConfig.host, i18nOptions);
for (const alt of alternates) {
page
.ele('xhtml:link')
.att('rel', 'alternate')
.att('hreflang', alt.locale)
.att('href', alt.href);
}
}
}
const mapPath = `${resolve(outputDir)}/sitemap.xml`;
try {
console.log(`Writing sitemap at ${mapPath}`);
writeFileSync(mapPath, sitemap.end({ prettyPrint: true }));
}
catch (e) {
console.error(`Unable to write file at ${mapPath}`, e);
}
}
}
/**
* Generates hreflang alternate URLs for a given page URL.
* For a URL like `https://example.com/fr/about`, it produces alternates
* for all configured locales.
*/
export function getHreflangAlternates(pageUrl, host, i18n) {
const alternates = [];
const normalizedHost = host.replace(/\/+$/, '');
// Extract the path portion after the host
const path = pageUrl.replace(normalizedHost, '');
// Strip locale prefix to get the base path
const basePath = stripLocalePrefix(path, i18n.locales);
for (const locale of i18n.locales) {
const localizedPath = basePath === '/' || basePath === ''
? `/${locale}`
: `/${locale}${basePath}`;
alternates.push({
locale,
href: `${normalizedHost}${localizedPath}`,
});
}
// Add x-default pointing to the default locale variant
const defaultPath = basePath === '/' || basePath === ''
? `/${i18n.defaultLocale}`
: `/${i18n.defaultLocale}${basePath}`;
alternates.push({
locale: 'x-default',
href: `${normalizedHost}${defaultPath}`,
});
return alternates;
}
/**
* Strips a locale prefix from a URL path.
* E.g., '/fr/about' -> '/about', '/en' -> '/'
*/
export function stripLocalePrefix(path, locales) {
const segments = path.split('/').filter(Boolean);
if (segments.length > 0 && locales.includes(segments[0])) {
const rest = segments.slice(1).join('/');
return rest ? `/${rest}` : '/';
}
return path || '/';
}
function createXml(elementName, includeXhtml = false) {
const attrs = {
xmlns: 'https://www.sitemaps.org/schemas/sitemap/0.9',
};
if (includeXhtml) {
attrs['xmlns:xhtml'] = 'https://www.w3.org/1999/xhtml';
}
return create({ version: '1.0', encoding: 'UTF-8' })
.ele(elementName, attrs)
.com(`This file was automatically generated by Analog.`);
}
function checkSlash(host) {
const finalChar = host.slice(-1);
return finalChar === '/' ? '' : '/';
}
async function optionHasRoutes(routes) {
let routeList;
if (typeof routes === 'function') {
// returns an array or undefined
routeList = await routes();
}
else if (Array.isArray(routes)) {
// returns an array of strings
routeList = routes;
}
else {
// default it to an empty of array
routeList = [];
}
return routeList.filter(Boolean);
}
//# sourceMappingURL=build-sitemap.js.map