Source

Reference for createSource, every Source method, the page and tree types, and the tree helpers.

ts
import { createSource } from "pzzadocs";
import type { Source, SourceOptions, PageData, CompiledPage, PageTreeNode, SearchRecord, PageSuggestion } from "pzzadocs";

createSource is the server-side entry point. It takes a resolved config, scans contentDir, and returns an object whose methods read, order, compile, and export pages. Create it once in a module such as lib/source.ts and import that module from your route files.

Server only

The root entry imports server-only and throws when pulled into a client component. Import the source only from Server Components, layouts, generateStaticParams, generateMetadata, and route handlers.

createSource#

ts
function createSource(config: ResolvedDocsConfig, options?: SourceOptions): Source;

interface SourceOptions {
  /** Extra MDX components merged over defaultMdxComponents. */
  components?: MDXComponents;
}

Pass options.components to make your own React components available in every MDX file without imports, or to override one of the defaults:

lib/source.ts
import { createSource } from "pzzadocs";

import config from "@/docs.config";
import { Chart } from "@/components/chart";

export const source = createSource(config, { components: { Chart } });

Source#

ts
interface Source {
  getPages(locale?: string): Promise<PageData[]>;
  getPage(slug?: string[], locale?: string): Promise<CompiledPage | undefined>;
  getPageTree(locale?: string): Promise<PageTreeNode[]>;
  getSearchIndex(locale?: string): Promise<SearchRecord[]>;
  getNeighbours(slug?: string[], locale?: string): Promise<Neighbours>;
  getMarkdown(slug?: string[], locale?: string): Promise<string | undefined>;
  getLlmsTxt(locale?: string): Promise<string>;
  getLlmsFullTxt(locale?: string): Promise<string>;
  getSuggestions(pathname: string, limit?: number, locale?: string): Promise<PageSuggestion[]>;
}

Every method takes an optional locale. When given, the translated files for that locale (page.de.mdx and meta.de.json with the suffix strategy, contentDir/de/... with the directory strategy) are preferred and the default locale's files are used as fallbacks; every url in the result carries the prefix chosen by localePrefix. Omit it, or pass defaultLocale, for the default locale. See Localization.

getPages#

Returns every page with parsed frontmatter, in filesystem order. No MDX is compiled, so it is cheap. Use it in generateStaticParams.

ts
export async function generateStaticParams() {
  const pages = await source.getPages();
  return pages.map((page) => ({ slug: page.slug }));
}

getPage#

Resolves a slug to a compiled page. The slug is the array of path segments after baseUrl: [] (or no argument) for the index, ["guides", "theming"] for /docs/guides/theming. Returns undefined when no file matches, which is the signal to call notFound().

ts
const page = await source.getPage(["guides", "theming"]);

Compilation happens on the first call for a given file and is cached per process, keyed by file path and modification time.

getPageTree#

Builds the sidebar tree from the folder structure and meta.json manifests, including route groups, link items, separators, and roots. Pass it to DocsLayout and DocsPage. The tree is cached until any content file is added, removed, or changed.

getSearchIndex#

Compiles every page and returns the flat SearchRecord[] described in the search guide. Serve it as JSON from the route at search.indexUrl.

getNeighbours#

Returns the previous and next pages relative to the slug, in sidebar order, or an empty object when the slug does not resolve. DocsPage renders them as the pagination cards.

ts
const { previous, next } = await source.getNeighbours(["guides", "theming"]);

getMarkdown#

Returns the page as raw Markdown for assistants: frontmatter stripped, the title inserted as an H1 when the body has none, and every heading suffixed with its anchor as [#id]. Returns undefined for unknown slugs.

ts
const markdown = await source.getMarkdown(["guides", "llm"]);

getLlmsTxt#

Returns the contents of llms.txt: the site title and description followed by every page's title, URL, and description, grouped by the tree.

getLlmsFullTxt#

Returns the contents of llms-full.txt: the Markdown of every page concatenated in tree order.

getSuggestions#

Returns "did you mean" candidates for an unknown path, scored by token overlap and bigram similarity between the requested pathname and each page's slug and title; weak matches are dropped. Use it behind a not-found page. Reading request headers inside the root not-found.tsx would make every page dynamic, so serve the suggestions from a route handler and fetch them from a small client component that reads usePathname():

app/api/suggestions/route.ts
export async function GET(request: NextRequest) {
  const path = request.nextUrl.searchParams.get("path") ?? "";
  if (!path.startsWith("/")) return Response.json([], { status: 400 });
  return Response.json(await source.getSuggestions(path, 5));
}
ts
interface PageSuggestion {
  url: string;
  title: string;
  score: number;
}

PageData#

The frontmatter fields are spread directly onto the page object alongside the routing fields.

ts
interface PageFrontmatter {
  title: string;
  description?: string;
  icon?: string;
  full?: boolean;
  order?: number;
  tag?: string;
}

interface PageData extends PageFrontmatter {
  slug: string[];
  url: string;
  path: string;
  file: string;
  locale?: string;
  frontmatter: Record<string, unknown>;
  lastModified: Date;
}
NameTypeDefaultDescription
slugstring[]-Path segments after baseUrl. Empty for the index. Route-group folders are not included.
urlstring-Absolute route including baseUrl, for example /docs/guides/theming.
pathstring-Absolute file path on disk.
filestring-Source file path relative to contentDir, with extension. Useful for editUrl.
localestring-Locale of this file, or undefined for the default locale.
frontmatterRecord<string, unknown>-The full raw frontmatter, including custom keys.
lastModifiedDate-Last git commit time, filesystem mtime, or the epoch, depending on the lastModified config.

CompiledPage#

ts
interface TocItem {
  id: string;
  title: string;
  depth: number;
}

interface CompiledPage extends PageData {
  toc: TocItem[];
  content: ReactNode;
}

toc holds the headings within toc.minDepth and toc.maxDepth, honouring the [#id], [!toc], and [toc] controls. content is the compiled MDX as a React element; render it inside DocsBody.

PageTreeNode#

The tree is an array of nodes in display order. Every node has a stable $id safe to use as a React key, a name, and an optional icon name.

ts
interface PageTreePage {
  type: "page";
  $id: string;
  name: string;
  url: string;
  icon?: string;
  description?: string;
  external?: boolean;
}

interface PageTreeFolder {
  type: "folder";
  $id: string;
  name: string;
  icon?: string;
  index?: PageTreePage;
  url?: string;
  description?: string;
  collapsible?: boolean;
  defaultOpen?: boolean;
  root?: boolean | string;
  children: PageTreeNode[];
}

interface PageTreeSeparator {
  type: "separator";
  $id: string;
  name: string;
  icon?: string;
}

type PageTreeNode = PageTreePage | PageTreeFolder | PageTreeSeparator;

A folder's index is its clickable page (index.mdx or the pagesIndex entry) and url is a shortcut for index?.url. Link items from meta.json are page nodes; external is set for those that open in a new tab.

Neighbours#

ts
interface PageLink {
  name: string;
  url: string;
}

interface Neighbours {
  previous?: PageLink;
  next?: PageLink;
}

Tree helpers#

The functions that power breadcrumbs, pagination, and the root switcher are exported from the root entry for custom layouts:

FunctionReturns
flattenTree(tree)PageLink[] of every page in sidebar order.
getNeighboursFromTree(tree, url)Neighbours for the page at url.
getBreadcrumbsFromTree(tree, url)BreadcrumbItem[] ({ name, url? }) from the root to the page.
isActiveUrl(pathname, url, nested?)Whether pathname matches url, or starts with it when nested is true.
folderContains(folder, pathname)Whether a folder subtree contains the page at pathname.
getRootFolders(tree)Every folder with root set.
getActiveRoot(tree, pathname)The root folder containing pathname, if any.
getSidebarNodes(tree, pathname)The nodes the sidebar should list for pathname: the active root's children, or the non-root nodes.
projectUrlToRoot(tree, pathname, target)The equivalent URL inside target when both roots share a type and the page exists there, otherwise the target's index.

Compilation pipeline#

Each page goes through the same steps:

  1. Frontmatter is parsed and removed from the body.
  2. Heading controls ([#id], [!toc], [toc]) are applied.
  3. The body is compiled with the MDX compiler in function-body mode and evaluated on the server with defaultMdxComponents plus your own.
  4. GitHub Flavored Markdown extensions are enabled: tables, task lists, strikethrough, and autolinks.
  5. Headings receive stable slug ids; the configured depth range is collected into toc, and the page structure (headings and text blocks) is recorded for the search index.
  6. Fenced code blocks are highlighted with pzza-light and pzza-dark, notation transformers are applied, tab fences are merged, and package-install fences are expanded.
  7. The result is cached in memory keyed by file path and mtime. The tree and search index are keyed by a hash of the content directory listing.