diff --git a/apps/docs/public/robots.txt b/apps/docs/public/robots.txt index e9e57dc4..da835680 100644 --- a/apps/docs/public/robots.txt +++ b/apps/docs/public/robots.txt @@ -1,3 +1,4 @@ # https://www.robotstxt.org/robotstxt.html User-agent: * Disallow: +Sitemap: https://zerobyte.app/sitemap.xml diff --git a/apps/docs/src/routeTree.gen.ts b/apps/docs/src/routeTree.gen.ts index 6a5e345a..4231c503 100644 --- a/apps/docs/src/routeTree.gen.ts +++ b/apps/docs/src/routeTree.gen.ts @@ -9,10 +9,16 @@ // Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified. import { Route as rootRouteImport } from './routes/__root' +import { Route as SitemapDotxmlRouteImport } from './routes/sitemap[.]xml' import { Route as IndexRouteImport } from './routes/index' import { Route as DocsSplatRouteImport } from './routes/docs/$' import { Route as ApiSearchRouteImport } from './routes/api/search' +const SitemapDotxmlRoute = SitemapDotxmlRouteImport.update({ + id: '/sitemap.xml', + path: '/sitemap.xml', + getParentRoute: () => rootRouteImport, +} as any) const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', @@ -31,36 +37,47 @@ const ApiSearchRoute = ApiSearchRouteImport.update({ export interface FileRoutesByFullPath { '/': typeof IndexRoute + '/sitemap.xml': typeof SitemapDotxmlRoute '/api/search': typeof ApiSearchRoute '/docs/$': typeof DocsSplatRoute } export interface FileRoutesByTo { '/': typeof IndexRoute + '/sitemap.xml': typeof SitemapDotxmlRoute '/api/search': typeof ApiSearchRoute '/docs/$': typeof DocsSplatRoute } export interface FileRoutesById { __root__: typeof rootRouteImport '/': typeof IndexRoute + '/sitemap.xml': typeof SitemapDotxmlRoute '/api/search': typeof ApiSearchRoute '/docs/$': typeof DocsSplatRoute } export interface FileRouteTypes { fileRoutesByFullPath: FileRoutesByFullPath - fullPaths: '/' | '/api/search' | '/docs/$' + fullPaths: '/' | '/sitemap.xml' | '/api/search' | '/docs/$' fileRoutesByTo: FileRoutesByTo - to: '/' | '/api/search' | '/docs/$' - id: '__root__' | '/' | '/api/search' | '/docs/$' + to: '/' | '/sitemap.xml' | '/api/search' | '/docs/$' + id: '__root__' | '/' | '/sitemap.xml' | '/api/search' | '/docs/$' fileRoutesById: FileRoutesById } export interface RootRouteChildren { IndexRoute: typeof IndexRoute + SitemapDotxmlRoute: typeof SitemapDotxmlRoute ApiSearchRoute: typeof ApiSearchRoute DocsSplatRoute: typeof DocsSplatRoute } declare module '@tanstack/react-router' { interface FileRoutesByPath { + '/sitemap.xml': { + id: '/sitemap.xml' + path: '/sitemap.xml' + fullPath: '/sitemap.xml' + preLoaderRoute: typeof SitemapDotxmlRouteImport + parentRoute: typeof rootRouteImport + } '/': { id: '/' path: '/' @@ -87,6 +104,7 @@ declare module '@tanstack/react-router' { const rootRouteChildren: RootRouteChildren = { IndexRoute: IndexRoute, + SitemapDotxmlRoute: SitemapDotxmlRoute, ApiSearchRoute: ApiSearchRoute, DocsSplatRoute: DocsSplatRoute, } diff --git a/apps/docs/src/routes/sitemap[.]xml.ts b/apps/docs/src/routes/sitemap[.]xml.ts new file mode 100644 index 00000000..63e42e8f --- /dev/null +++ b/apps/docs/src/routes/sitemap[.]xml.ts @@ -0,0 +1,39 @@ +import { createFileRoute } from "@tanstack/react-router"; + +import { source } from "@/lib/source"; + +const siteUrl = "https://zerobyte.app"; + +function escapeXml(value: string) { + return value + .replaceAll("&", "&") + .replaceAll("<", "<") + .replaceAll(">", ">") + .replaceAll('"', """) + .replaceAll("'", "'"); +} + +function buildSitemapXml(urls: string[]) { + const entries = urls.map((url) => ` \n ${escapeXml(url)}\n `).join("\n"); + + return `\n\n${entries}\n`; +} + +export const Route = createFileRoute("/sitemap.xml")({ + server: { + handlers: { + GET: async () => { + const urls = Array.from(new Set(["/", ...source.getPages().map((page) => page.url)])) + .sort() + .map((path) => new URL(path, siteUrl).toString()); + + return new Response(buildSitemapXml(urls), { + headers: { + "Content-Type": "application/xml; charset=utf-8", + "Cache-Control": "public, max-age=3600", + }, + }); + }, + }, + }, +});