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();