Skip to content

Commit

Permalink
basepath handler for ~ routes
Browse files Browse the repository at this point in the history
  • Loading branch information
abvthecity committed Feb 6, 2025
1 parent 1f8509a commit a6f936b
Show file tree
Hide file tree
Showing 9 changed files with 76 additions and 25 deletions.
1 change: 1 addition & 0 deletions packages/fern-docs/bundle/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@
"cssnano": "^6.0.3",
"es-toolkit": "^1.27.0",
"esbuild": "0.24.2",
"escape-string-regexp": "^5.0.0",
"estree-walker": "^3.0.3",
"fastdom": "^1.0.12",
"feed": "^4.2.2",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,27 +1,41 @@
"use server";

import { PlaygroundEndpoint } from "@/components/playground/endpoint/PlaygroundEndpoint";
import { conformExplorerRoute } from "@/components/playground/utils/explorer-route";
import { PlaygroundWebSocket } from "@/components/playground/websocket/PlaygroundWebSocket";
import { createCachedDocsLoader } from "@/server/docs-loader";
import { createFindNode } from "@/server/find-node";
import { getDocsDomainApp } from "@/server/xfernhost/app";
import { FernNavigation } from "@fern-api/fdr-sdk";
import {
createEndpointContext,
createWebSocketContext,
} from "@fern-api/fdr-sdk/api-definition";
import { COOKIE_FERN_TOKEN } from "@fern-docs/utils";
import { cookies } from "next/headers";
import { notFound } from "next/navigation";
import { conformTrailingSlash, COOKIE_FERN_TOKEN } from "@fern-docs/utils";
import { cookies, headers } from "next/headers";
import { notFound, redirect } from "next/navigation";

export default async function Page({ params }: { params: { slug: string[] } }) {
const slug = FernNavigation.slugjoin(params.slug);
const slug = FernNavigation.slugjoin(
headers().get("x-basepath"),
params.slug
);
const fern_token = cookies().get(COOKIE_FERN_TOKEN)?.value;
const loader = await createCachedDocsLoader(getDocsDomainApp(), fern_token);
const node = await createFindNode(loader)(slug);
if (node == null) {
const root = await loader.getRoot();
if (root == null) {
notFound();
}
const found = FernNavigation.utils.findNode(root, slug);
if (found.type !== "found") {
if (found.redirect) {
// follows the route path hierarchy
// e.g. /docs/foo/bar -> /docs/~/api-explorer/foo/bar
redirect(conformTrailingSlash(conformExplorerRoute(slug, root.slug)));
}

notFound();
}
const node = found.node;
if (!FernNavigation.isApiLeaf(node)) {
notFound();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export default async function Layout({
<PlaygroundEndpointSelectorContent
apiGroups={apiGroups}
className="h-full"
rootslug={root.slug}
/>
{children}
</HorizontalSplitPane>
Expand Down
2 changes: 2 additions & 0 deletions packages/fern-docs/bundle/src/app/not-found.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
conformTrailingSlash,
DEFAULT_LOGO_HEIGHT,
} from "@fern-docs/utils";
import { Undo2 } from "lucide-react";

export default async function NotFound() {
const loader = await createCachedDocsLoader(getDocsDomainApp());
Expand Down Expand Up @@ -45,6 +46,7 @@ export default async function NotFound() {
)}
variant="filled"
intent="primary"
rightIcon={<Undo2 />}
>
Return Home
</FernLinkButton>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,22 +12,17 @@ import { removeTrailingSlash } from "@fern-docs/utils";
import cn, { clsx } from "clsx";
import { Search, Slash, Xmark } from "iconoir-react";
import { usePathname } from "next/navigation";
import {
Fragment,
forwardRef,
useEffect,
useImperativeHandle,
useRef,
useState,
} from "react";
import { Fragment, forwardRef, useEffect, useRef, useState } from "react";
import { BuiltWithFern } from "../../sidebar/BuiltWithFern";
import { conformExplorerRoute } from "../utils/explorer-route";
import { ApiGroup } from "../utils/flatten-apis";
import { PlaygroundEndpointSelectorLeafNode } from "./PlaygroundEndpointSelectorLeafNode";

export interface PlaygroundEndpointSelectorContentProps {
apiGroups: ApiGroup[];
className?: string;
shallow?: boolean;
rootslug: FernNavigation.Slug;
}

function matchesEndpoint(
Expand All @@ -49,11 +44,8 @@ function matchesEndpoint(
export const PlaygroundEndpointSelectorContent = forwardRef<
HTMLDivElement,
PlaygroundEndpointSelectorContentProps
>(({ apiGroups, className, shallow }, ref) => {
>(({ apiGroups, className, shallow, rootslug }, forwardedRef) => {
const pathname = usePathname();
const scrollRef = useRef<HTMLDivElement>(null);
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
useImperativeHandle(ref, () => scrollRef.current!);

const [filterValue, setFilterValue] = useState<string>("");

Expand Down Expand Up @@ -94,7 +86,8 @@ export const PlaygroundEndpointSelectorContent = forwardRef<
<ul className="relative z-0 list-none">
{apiLeafNodes.map((node) => {
const active =
removeTrailingSlash(pathname) === `/~/api-explorer/${node.slug}`;
removeTrailingSlash(pathname) ===
conformExplorerRoute(node.slug, rootslug);
return (
<PlaygroundEndpointSelectorLeafNode
key={node.id}
Expand All @@ -103,6 +96,7 @@ export const PlaygroundEndpointSelectorContent = forwardRef<
ref={active ? selectedItemRef : undefined}
filterValue={filterValue}
shallow={shallow}
rootslug={rootslug}
/>
);
})}
Expand All @@ -118,7 +112,7 @@ export const PlaygroundEndpointSelectorContent = forwardRef<
<FernTooltipProvider>
<div
className={clsx("relative flex size-full flex-col", className)}
ref={scrollRef}
ref={forwardedRef}
>
<div className={cn("relative z-20 px-3 pb-0 pt-3")}>
<FernInput
Expand All @@ -144,7 +138,6 @@ export const PlaygroundEndpointSelectorContent = forwardRef<
className="mask-grad-y-6 !flex w-full"
scrollbars="vertical"
asChild
ref={ref}
>
<ul className="flex h-fit w-full list-none flex-col gap-4 p-3">
{renderedListItems}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,20 @@ import { ReactElement, forwardRef } from "react";
import { useMemoOne } from "use-memo-one";
import { getApiDefinitionAtom } from "../../atoms";
import { Markdown } from "../../mdx/Markdown";
import { conformExplorerRoute } from "../utils/explorer-route";

interface PlaygroundEndpointSelectorLeafNodeProps {
node: FernNavigation.EndpointNode | FernNavigation.WebSocketNode;
filterValue: string;
active: boolean;
shallow?: boolean;
rootslug: FernNavigation.Slug;
}

export const PlaygroundEndpointSelectorLeafNode = forwardRef<
HTMLLIElement,
PlaygroundEndpointSelectorLeafNodeProps
>(({ node, filterValue, active, shallow }, ref) => {
>(({ node, filterValue, active, shallow, rootslug }, ref) => {
const text = renderTextWithHighlight(node.title, filterValue);

const description = useAtomValue(
Expand Down Expand Up @@ -49,7 +51,7 @@ export const PlaygroundEndpointSelectorLeafNode = forwardRef<
<li ref={ref}>
<FernTooltip content={tooltipContent} side="right">
<FernLinkButton
href={`/~/api-explorer/${node.slug}`}
href={conformExplorerRoute(node.slug, rootslug)}
shallow={shallow}
text={text}
className="w-full text-left"
Expand Down Expand Up @@ -77,7 +79,7 @@ export const PlaygroundEndpointSelectorLeafNode = forwardRef<
<li ref={ref}>
<FernTooltip content={tooltipContent} side="right">
<FernLinkButton
href={`/~/api-explorer/${node.slug}`}
href={conformExplorerRoute(node.slug, rootslug)}
shallow={shallow}
text={text}
className="w-full text-left"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { FernNavigation } from "@fern-api/fdr-sdk";
import { addLeadingSlash } from "@fern-docs/utils";
import escapeStringRegexp from "escape-string-regexp";

/**
* Conforms the slug to the explorer route.
*
* For example, if the original slug is "docs/foo/bar" and the root slug is "docs",
* the explorer route is "/docs/~/api-explorer/foo/bar".
*/
export function conformExplorerRoute(
slug: FernNavigation.Slug,
rootslug: FernNavigation.Slug
): string {
return addLeadingSlash(
FernNavigation.slugjoin(
rootslug,
"~/api-explorer",
slug.replace(new RegExp(`^${escapeStringRegexp(rootslug)}/`), "")
)
);
}
13 changes: 13 additions & 0 deletions packages/fern-docs/bundle/src/middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,19 @@ export const middleware: NextMiddleware = async (request) => {
);
}

/**
* Rewrite .../~/... to /~/...
*/
if (!pathname.startsWith("/~") && pathname.includes("/~/")) {
const index = pathname.indexOf("/~/");
pathname = pathname.slice(index);
const basepath = pathname.slice(0, index);
headers.set("x-basepath", basepath);
return NextResponse.rewrite(withPathname(request, pathname), {
request: { headers },
});
}

return NextResponse.next({
request: { headers },
});
Expand Down
3 changes: 3 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit a6f936b

Please sign in to comment.