From b4a302edfaa035ed60664e849e8a7d8488e7a659 Mon Sep 17 00:00:00 2001 From: Luca Martial Date: Mon, 11 May 2026 20:37:28 -0700 Subject: [PATCH] fix(docs-site): add docs index redirect, test, and trim Accept media type Redirect bare /docs to the introduction page with proper metadata resolution. Add a test for the redirect. Trim the media type in the Accept header parser so whitespace around semicolons does not break markdown content negotiation. Add a test script to package.json. Co-Authored-By: Claude Opus 4.6 (1M context) --- docs-site/app/docs/[[...slug]]/page.tsx | 19 ++++++++++++++++--- docs-site/middleware.ts | 2 +- docs-site/package.json | 3 ++- docs-site/tests/docs-index-route.test.mjs | 14 ++++++++++++++ 4 files changed, 33 insertions(+), 5 deletions(-) create mode 100644 docs-site/tests/docs-index-route.test.mjs diff --git a/docs-site/app/docs/[[...slug]]/page.tsx b/docs-site/app/docs/[[...slug]]/page.tsx index 0b59743d..869454ec 100644 --- a/docs-site/app/docs/[[...slug]]/page.tsx +++ b/docs-site/app/docs/[[...slug]]/page.tsx @@ -5,15 +5,26 @@ import { DocsTitle, DocsDescription, } from "fumadocs-ui/page"; -import { notFound } from "next/navigation"; +import { notFound, redirect } from "next/navigation"; import defaultMdxComponents from "fumadocs-ui/mdx"; import { CodeBlock } from "@/components/code-block"; import { DocsPageActions } from "@/components/docs-page-actions"; +const docsIndexPath = "/docs/getting-started/introduction"; +const docsIndexSlug = ["getting-started", "introduction"] as const; + +function isDocsIndex(slug: string[] | undefined) { + return slug === undefined || slug.length === 0 || slug.join("/") === ""; +} + export default async function Page(props: { params: Promise<{ slug?: string[] }>; }) { const params = await props.params; + if (isDocsIndex(params.slug)) { + redirect(docsIndexPath); + } + const page = source.getPage(params.slug); if (!page) notFound(); @@ -35,14 +46,16 @@ export default async function Page(props: { } export function generateStaticParams() { - return source.generateParams(); + return [{ slug: [""] }, ...source.generateParams()]; } export async function generateMetadata(props: { params: Promise<{ slug?: string[] }>; }) { const params = await props.params; - const page = source.getPage(params.slug); + const page = source.getPage( + isDocsIndex(params.slug) ? [...docsIndexSlug] : params.slug, + ); if (!page) notFound(); return { diff --git a/docs-site/middleware.ts b/docs-site/middleware.ts index 4e06ea8b..670ebd33 100644 --- a/docs-site/middleware.ts +++ b/docs-site/middleware.ts @@ -38,7 +38,7 @@ function isMarkdownPreferred(acceptHeader: string | null) { .find((parameter) => parameter.startsWith("q=")); return { - type: type.toLowerCase(), + type: type.trim().toLowerCase(), quality: quality ? Number.parseFloat(quality.slice(2)) : 1, index, }; diff --git a/docs-site/package.json b/docs-site/package.json index 8ab53b93..d4a88999 100644 --- a/docs-site/package.json +++ b/docs-site/package.json @@ -6,7 +6,8 @@ "scripts": { "dev": "next dev", "build": "next build", - "start": "next start" + "start": "next start", + "test": "node --test tests/*.test.mjs" }, "dependencies": { "fumadocs-core": "15.7.13", diff --git a/docs-site/tests/docs-index-route.test.mjs b/docs-site/tests/docs-index-route.test.mjs new file mode 100644 index 00000000..859ae54e --- /dev/null +++ b/docs-site/tests/docs-index-route.test.mjs @@ -0,0 +1,14 @@ +import assert from "node:assert/strict"; +import test from "node:test"; + +const docsSiteUrl = process.env.DOCS_SITE_URL ?? "http://localhost:3000"; + +test("/docs redirects to the docs introduction", async () => { + const response = await fetch(`${docsSiteUrl}/docs`, { redirect: "manual" }); + + assert.equal(response.status, 307); + assert.equal( + response.headers.get("location"), + "/docs/getting-started/introduction", + ); +});