UNPKG

@analogjs/vite-plugin-nitro

Version:

A Vite plugin for adding a nitro API server

127 lines 4.72 kB
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