diff --git a/app/sitemap.ts b/app/sitemap.ts index 45b6615cf..2677eea14 100644 --- a/app/sitemap.ts +++ b/app/sitemap.ts @@ -1,6 +1,7 @@ import fs from "node:fs/promises"; import path from "node:path"; import type { MetadataRoute } from "next"; +import { listToolkitRoutes } from "./_lib/toolkit-static-params"; const SITE_URL = process.env.SITE_URL ?? "https://docs.arcade.dev"; const NORMALIZED_SITE_URL = SITE_URL.replace(/\/+$/, ""); @@ -43,13 +44,33 @@ async function collectRoutes(dir: string): Promise { return entries; } +async function buildSitemap(): Promise { + const [mdxRoutes, toolkitRoutes] = await Promise.all([ + collectRoutes(APP_DIR), + listToolkitRoutes(), + ]); + + const toolkitEntries: MetadataRoute.Sitemap = toolkitRoutes + .filter((route) => route.category !== "others") + .map((route) => ({ + url: `${NORMALIZED_SITE_URL}/en/resources/integrations/${route.category}/${route.toolkitId}`, + })); + + const seen = new Set(mdxRoutes.map((r) => r.url)); + const uniqueToolkitEntries = toolkitEntries.filter((entry) => { + if (seen.has(entry.url)) { return false; } + seen.add(entry.url); + return true; + }); + + return [...mdxRoutes, ...uniqueToolkitEntries].sort((a, b) => + a.url.localeCompare(b.url) + ); +} + export default function sitemap(): Promise { if (!cachedRoutes) { - cachedRoutes = collectRoutes(APP_DIR).then((routes) => { - routes.sort((a, b) => a.url.localeCompare(b.url)); - return routes; - }); + cachedRoutes = buildSitemap(); } - return cachedRoutes; } diff --git a/tests/sitemap.test.ts b/tests/sitemap.test.ts index 1bd5a556b..3ceb0bb75 100644 --- a/tests/sitemap.test.ts +++ b/tests/sitemap.test.ts @@ -19,9 +19,21 @@ test("sitemap lists expected URLs", async () => { true ); - // Known page should be present + // Known static page should be present expect(urls).toContain("https://example.test/en/references/changelog"); + // Toolkit pages should be included + expect(urls).toContain( + "https://example.test/en/resources/integrations/development/github" + ); + const integrationUrls = urls.filter((url) => + url.includes("/en/resources/integrations/") + ); + expect(integrationUrls.length).toBeGreaterThan(10); + + // No dynamic-segment placeholders should leak into output + expect(urls.every((url) => !url.includes("["))).toBe(true); + // No duplicates const duplicates = urls.filter( (url, index, arr) => arr.indexOf(url) !== index