UNPKG

alepha

Version:

Easy-to-use modern TypeScript framework for building many kind of applications.

131 lines (130 loc) 4.5 kB
import { $inject, $module, KIND, Primitive, createPrimitive } from "alepha"; import { AlephaDateTime, DateTimeProvider } from "alepha/datetime"; import { AlephaServer, ServerRouterProvider } from "alepha/server"; //#region ../../src/react/sitemap/primitives/$sitemap.ts /** * Expose a `sitemap.xml` generated from the application's `$page` primitives. * * Registers a `GET /sitemap.xml` route that reads every registered page at * request time and emits a standard XML sitemap. Marked `static` by default, so * the build prerenders it to `dist/public/sitemap.xml` for static deployments — * while SSR runtimes also serve it live. * * The hostname comes from `options.hostname`, falling back to `PUBLIC_URL`, then * to `""` (relative URLs). * * @example * ```ts * import { $sitemap } from "alepha/react/sitemap"; * * class AppRouter { * sitemap = $sitemap(); * } * ``` */ const $sitemap = (options = {}) => { return createPrimitive(SitemapPrimitive, options); }; var SitemapPrimitive = class extends Primitive { router = $inject(ServerRouterProvider); dateTime = $inject(DateTimeProvider); onInit() { this.router.createRoute({ method: "GET", path: this.options.path ?? "/sitemap.xml", static: this.options.static ?? true, silent: true, handler: (request) => { request.reply.setHeader("content-type", "application/xml"); return this.buildSitemap(); } }); } /** * Render the sitemap to its path and body. Used by the build to snapshot the * sitemap to a static file. */ prerender() { return { path: this.options.path ?? "/sitemap.xml", body: this.buildSitemap() }; } /** * Build the sitemap XML from the application's page primitives. */ buildSitemap() { const hostname = this.options.hostname ?? String(this.alepha.env.PUBLIC_URL ?? ""); const pages = this.getSitemapPages(); return this.generateSitemapFromPages(pages, hostname); } /** * Select the pages that should appear in the sitemap. * * Excludes layout pages (with `children`), wildcard paths, and `/404`. * Parameterized pages are included only when they declare `static.entries`. */ getSitemapPages() { return this.alepha.primitives("page").filter((page) => { const options = page.options; const path = options.path ?? ""; if (options.children) return false; if (path.includes("*")) return false; if (path === "/404") return false; if (!options.schema?.params) return true; if (options.static && typeof options.static === "object" && options.static.entries) return true; return false; }); } generateSitemapFromPages(pages, baseUrl) { const urls = []; const normalizedBaseUrl = baseUrl.replace(/\/$/, ""); for (const page of pages) { const options = page.options; if (!options.schema?.params) { const path = options.path || ""; const url = `${normalizedBaseUrl}${path === "" ? "/" : path}`; urls.push(url); } else if (options.static && typeof options.static === "object" && options.static.entries) for (const entry of options.static.entries) { const url = `${normalizedBaseUrl}${this.buildPathFromParams(options.path || "", entry.params || {})}`; urls.push(url); } } return this.buildSitemapXml(urls); } buildPathFromParams(pathPattern, params) { let path = pathPattern; for (const [key, value] of Object.entries(params)) path = path.replace(`:${key}`, String(value)); return path || "/"; } buildSitemapXml(urls) { const lastMod = this.dateTime.now().format("YYYY-MM-DD"); return `<?xml version="1.0" encoding="UTF-8"?> <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"> ${urls.map((url) => ` <url>\n <loc>${this.escapeXml(url)}</loc>\n <lastmod>${lastMod}</lastmod>\n </url>`).join("\n")} </urlset>`; } escapeXml(str) { return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;"); } }; $sitemap[KIND] = SitemapPrimitive; //#endregion //#region ../../src/react/sitemap/index.ts /** * Sitemap generation for React applications. * * Exposes the {@link $sitemap} primitive, which serves a `sitemap.xml` built * from the app's `$page` primitives — live at request time and prerendered to a * static file at build time. * * @module alepha.react.sitemap */ const AlephaReactSitemap = $module({ name: "alepha.react.sitemap", imports: [AlephaServer, AlephaDateTime], primitives: [$sitemap] }); //#endregion export { $sitemap, AlephaReactSitemap, SitemapPrimitive }; //# sourceMappingURL=index.js.map