import { navigation } from "@/lib/navigation"; import path from "path"; import fs from "fs"; const __dirname = path.resolve(); const getBaseUrl = () => { if (process.env.NODE_ENV === "production") { return "https://memento-dev.fr"; } return `http://localhost:${process.env.PORT || 3000}`; }; type SitemapElement = { location: string; lastmod: string; priority: string; }; class Sitemap { private readonly pagesPath = path.join(__dirname, "pages"); private readonly dataPath = path.join(__dirname, "data"); private readonly sitemapPath = path.join(__dirname, "public", "sitemap.xml"); private readonly lastModified = new Date().toISOString(); private readonly baseUrl = getBaseUrl(); private urls: SitemapElement[] = []; private sitemap: string = ""; private static instance: Sitemap; private constructor() {} public static getInstance(): Sitemap { if (!Sitemap.instance) { Sitemap.instance = new Sitemap(); } return Sitemap.instance; } private resetMemory(): void { this.sitemap = ""; this.urls = []; } private prependSitemap(): void { this.sitemap = ``; this.sitemap += ``; } private appendSitemap(): void { this.sitemap += ``; } private addSitemapElement(url: SitemapElement): void { this.sitemap += ``; this.sitemap += `${url.location}`; this.sitemap += `${url.lastmod || this.lastModified}`; this.sitemap += `${url.priority}`; this.sitemap += ``; } private buildSitemap(): void { this.prependSitemap(); this.urls.forEach(this.addSitemapElement.bind(this)); this.appendSitemap(); } private saveSitemap(): void { fs.writeFileSync(this.sitemapPath, this.sitemap, "utf8"); } private loadPriority(href: string): string { const isRootUrl = ["/", ""].includes(href); if (isRootUrl) return "1.0"; const countOfSlashes = (href.match(/\//g) || []).length; return (1 - countOfSlashes * 0.1).toFixed(1); } private loadLastModified(stat?: fs.Stats): string { return stat ? stat.mtime.toISOString() : this.lastModified; } private getFileServerLocation(href: string) { const jsxHref = ["/politique-de-confidentialite", "/mentions-legales"]; const isJsxFile = jsxHref.includes(href); if (isJsxFile) { return path.join(this.pagesPath, href.replace("/", ""), "+Page.tsx"); } return path.join(this.dataPath, href.replace("/", ""), "page.md"); } private loadSubitems(subitems: (typeof navigation)[number]["links"][number]["subitems"]): void { subitems.forEach((subitem) => { const fileLocation = this.getFileServerLocation(subitem.href); let fileDetails: fs.Stats | undefined; try { fileDetails = fs.statSync(fileLocation); } catch (error) { console.error(`Error loading file for ${subitem.href}:`, error); } const priority = this.loadPriority(subitem.href); const lastmod = this.loadLastModified(fileDetails); const location = `${this.baseUrl}${subitem.href}`; this.urls.push({ location, lastmod, priority, }); }); } private loadSection(section: (typeof navigation)[number]): void { section.links.forEach((link) => { if (link.subitems.length > 0) { return this.loadSubitems(link.subitems); } const fileLocation = this.getFileServerLocation(link.href); let fileDetails: fs.Stats | undefined; try { fileDetails = fs.statSync(fileLocation); } catch (error) { console.error(`Error loading file for ${link.href}:`, error); } const priority = this.loadPriority(link.href); const lastmod = this.loadLastModified(fileDetails); const location = `${this.baseUrl}${link.href}`; this.urls.push({ location, lastmod, priority, }); }); } private loadUrls(): void { navigation.forEach(this.loadSection.bind(this)); this.urls = Array.from(new Set(this.urls)).sort((a, b) => { return a.location.localeCompare(b.location); }); } public generateSitemap(): void { console.log("Generating sitemap..."); this.resetMemory(); this.loadUrls(); this.buildSitemap(); this.saveSitemap(); console.log("Sitemap generated successfully."); } } export const sitemap = Sitemap.getInstance();