Skip to content

Commit

Permalink
feat: support raw markdown (#1820)
Browse files Browse the repository at this point in the history
  • Loading branch information
abvthecity authored Nov 16, 2024
1 parent 7a9205c commit f8b70aa
Show file tree
Hide file tree
Showing 3 changed files with 112 additions and 0 deletions.
16 changes: 16 additions & 0 deletions packages/ui/docs-bundle/src/middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { withPathname } from "./server/withPathname";

const API_FERN_DOCS_PATTERN = /^(?!\/api\/fern-docs\/).*(\/api\/fern-docs\/)/;
const CHANGELOG_PATTERN = /\.(rss|atom)$/;
const MARKDOWN_PATTERN = /\.(md|mdx)$/;

export const middleware: NextMiddleware = async (request) => {
let pathname = extractNextDataPathname(removeTrailingSlash(request.nextUrl.pathname));
Expand Down Expand Up @@ -71,12 +72,27 @@ export const middleware: NextMiddleware = async (request) => {
const changelogFormat = pathname.match(CHANGELOG_PATTERN)?.[1];
if (changelogFormat != null) {
pathname = pathname.replace(new RegExp(`.${changelogFormat}$`), "");
if (pathname === "/index") {
pathname = "/";
}
const url = new URL("/api/fern-docs/changelog", request.nextUrl.origin);
url.searchParams.set("format", changelogFormat);
url.searchParams.set("path", pathname);
return NextResponse.rewrite(String(url));
}

const markdownExtension = pathname.match(MARKDOWN_PATTERN)?.[1];
if (markdownExtension != null) {
pathname = pathname.replace(new RegExp(`.${markdownExtension}$`), "");
if (pathname === "/index") {
pathname = "/";
}
const url = new URL("/api/fern-docs/markdown", request.nextUrl.origin);
url.searchParams.set("format", markdownExtension);
url.searchParams.set("path", pathname);
return NextResponse.rewrite(String(url));
}

return withMiddlewareAuth(request, pathname, withMiddlewareRewrite(request, pathname));
};

Expand Down
93 changes: 93 additions & 0 deletions packages/ui/docs-bundle/src/pages/api/fern-docs/markdown.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import { DocsLoader } from "@/server/DocsLoader";
import { removeLeadingSlash } from "@/server/removeLeadingSlash";
import { getDocsDomainNode, getHostNode } from "@/server/xfernhost/node";
import { FernNavigation } from "@fern-api/fdr-sdk";
import { getFrontmatter } from "@fern-ui/fern-docs-mdx";
import { COOKIE_FERN_TOKEN } from "@fern-ui/fern-docs-utils";
import { NextApiRequest, NextApiResponse } from "next";

function getStringParam(req: NextApiRequest, param: string): string | undefined {
const value = req.query[param];
return typeof value === "string" ? value : undefined;
}

export default async function handler(req: NextApiRequest, res: NextApiResponse): Promise<unknown> {
const path = getStringParam(req, "path");
const format = getStringParam(req, "format");

if (path == null || format == null) {
return res.status(400).end();
}

if (req.method === "OPTIONS") {
return res.status(200).setHeader("X-Robots-Tag", "noindex").setHeader("Allow", "OPTIONS, GET").end();
}

if (req.method !== "GET") {
return res.status(405).end();
}

const domain = getDocsDomainNode(req);
const host = getHostNode(req) ?? domain;
const fern_token = req.cookies[COOKIE_FERN_TOKEN];
const loader = DocsLoader.for(domain, host, fern_token);

const root = await loader.root();
const pages = await loader.pages();

const pageInfo = getPageInfo(root, FernNavigation.Slug(removeLeadingSlash(path)));

if (pageInfo == null) {
return res.status(404).end();
}

const extension = pageInfo.pageId.endsWith(".mdx") ? "mdx" : "md";

if (format !== extension) {
return res.status(404).end();
}

const page = pages[pageInfo.pageId];

if (!page) {
return res.status(404).end();
}

const { data: frontmatter, content } = getFrontmatter(page.markdown);

// TODO: parse the first h1 as the title
const title = frontmatter.title ?? pageInfo.nodeTitle;

res.status(200)
.setHeader("Content-Type", `text/${extension === "mdx" ? "mdx" : "markdown"}`)
// prevent search engines from indexing this page
.setHeader("X-Robots-Tag", "noindex")
// cannot guarantee that the content won't change, so we only cache for 60 seconds
.setHeader("Cache-Control", "s-maxage=60");

return res.send(`# ${title}\n\n${content}`);
}

function getPageInfo(
root: FernNavigation.RootNode | undefined,
slug: FernNavigation.Slug,
): { pageId: FernNavigation.PageId; nodeTitle: string } | undefined {
if (root == null) {
return undefined;
}

const foundNode = FernNavigation.utils.findNode(root, slug);
if (foundNode == null || foundNode.type !== "found" || !FernNavigation.hasMarkdown(foundNode.node)) {
return undefined;
}

const pageId = FernNavigation.getPageId(foundNode.node);
if (pageId == null) {
return undefined;
}

return {
pageId,
nodeTitle: foundNode.node.title,
};
}
3 changes: 3 additions & 0 deletions packages/ui/docs-bundle/src/server/removeLeadingSlash.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export function removeLeadingSlash(path: string): string {
return path.startsWith("/") ? path.slice(1) : path;
}

0 comments on commit f8b70aa

Please sign in to comment.