@farming-labs/farmjs
The official Farm.js runtime adapter for
@farming-labs/docs.
Install
npm install @farming-labs/docs @farming-labs/farmjs @farming-labs/theme
Configure Farm
Keep documentation settings in docs.config.ts, then enable the adapter from
farm.config.ts:
import { defineConfig } from "@farm.js/core";
import { withDocs } from "@farming-labs/farmjs/config";
export default withDocs(
defineConfig({
preset: "vercel",
}),
);
Farm discovers docs.config.ts from the application root. The wrapper writes
an adapter runtime descriptor into Farm's docs configuration. Farm uses that
versioned contract to load page rendering, server behavior, and MDX integration
from @farming-labs/farmjs without selecting a docs theme itself. A different path can
be provided explicitly:
export default withDocs(defineConfig({}), {
configPath: "config/docs.config.ts",
});
Code highlighting is compiled by the adapter's MDX pipeline. Pick a specific Shiki palette at that boundary without replacing the rest of the adapter:
export default withDocs(defineConfig({}), {
codeBlockThemes: {
light: "github-light-default",
dark: "vesper",
},
});
Add the application's own MDX plugins in mdx in docs.config.ts. Each plugin is a
package name or a path relative to the application root, with optional JSON options:
export default defineDocs({
entry: "docs",
mdx: {
remarkPlugins: ["./lib/remark-docs.mjs"],
rehypePlugins: [["rehype-katex", { strict: false }]],
codeHighlighter: "./lib/rehype-docs-code.mjs",
},
});
remarkPluginsandrehypePluginsrun after the built-in plugins.codeHighlighterreplaces only the syntax highlighter; metadata and the table of contents stay enabled.- Node and edge targets compile pages with them.
- Production builds compile every page while
mdxis set, andbuildCache: truealongside it throws. withDocs(config, { config: { mdx } })replaces the file'smdx.
Import the selected theme from the application-owned global stylesheet. This also gives the application a normal place for project-specific overrides:
/* src/app/globals.css */
@import "@farming-labs/theme/pixel-border/css";
:root {
--color-fd-primary: #8b5cf6;
}
The theme factory in docs.config.ts and the CSS entrypoint must describe the
same theme. This is the same unified CSS entrypoint used by the Next.js,
TanStack Start, SvelteKit, Astro, and Nuxt integrations. The adapter does not
ship or inject a second Farm-specific theme stylesheet.
Repeated production builds
The adapter reuses unchanged Markdown/MDX compilation and syntax highlighting
in production builds. It stores the compiled code and source maps under
<Vite cacheDir>/farming-docs/mdx-v1.json; Vite normally places cacheDir in
node_modules/.vite. Preserve that directory in your CI build cache to reuse
work across jobs. Deleting it only causes compilation to run again.
Cache entries are keyed by source, module path, mode, highlighting themes, Node version, and a content fingerprint of the compiler implementation and its dependencies. Vite still discovers and builds imports, components, CSS, routes, and deployment output normally. Development and watch builds do not use this cache. Missing, corrupt, incompatible, or unwritable cache files fall back to compilation; compiler errors still fail the build.
The cache retains at most 1,024 modules and approximately 32 MiB of payload. It does not cache rendered HTML or change client navigation or prerendering. To diagnose a build without MDX cache reuse:
export default withDocs(defineConfig({}), { buildCache: false });
The lower-level docsMdx({ buildCache: false }) option has the same behavior.
Prerendered pages
Set docs.prerender: true in farm.config.ts to have farm build render every
docs page into a static file on Node and serverless targets:
export default withDocs(defineConfig({ docs: { prerender: true } }));
Farm asks the adapter's server entry for its page list (listFarmDocsPages).
Prerendered pages link their Markdown copy relative to the page, or under the
configured sitemap, llmsTxt, or robots baseUrl, so social cards need
that base URL to stay absolute. Client navigation reads a static page's data
from its HTML. A static file cannot negotiate Accept: text/markdown, so agents
use the .md URL the page links to. Markdown, search, llms.txt, and MCP keep
rendering on request.
Guarded pages
Guard a docs page as you guard app pages: a middleware.ts in its folder, a
config middleware matcher, or an auth integration's protected routes. The
adapter declares the guarded-pages capability, so Farm serves such pages
with it and passes the app's guards to the runtime. A page a guard covers then
stays out of search, llms.txt, llms-full.txt, the sitemap, MCP, Ask AI, and
the content feed, which answer without running the guard. Its Markdown answers
at its own URL (Accept: text/markdown) and at a .md URL only when the guard
covers that URL too. Edge targets serve a snapshot that runs no guard, so Farm
stops an edge build that has guarded docs pages.
Edge deployments
Farm edge targets use the adapter's build-time compiler automatically. It precompiles pages, client-navigation data, CSS, Markdown, search, sitemap, robots, and agent-discovery responses so the deployed Worker does not import the Node server runtime.
The snapshot cannot preserve features that need live server callbacks or mutable sessions. Disable the default MCP and production telemetry surfaces in an edge-specific docs configuration:
import { defineDocs } from "@farming-labs/docs";
export default defineDocs({
entry: "docs",
mcp: false,
telemetry: false,
});
An edge build also stops with an actionable error when Ask AI, feedback, analytics, observability, i18n, or a function-backed custom search provider is enabled. Use a Node target when those runtime features are required.
Server wrapper
withDocs() is the normal integration. The lower-level server wrapper is
available for custom runtimes, route testing, and framework development:
import docsConfig from "./docs.config";
import { createDocsServer } from "@farming-labs/farmjs/server";
const docs = createDocsServer({
...docsConfig,
rootDir: process.cwd(),
});
const response = await docs.handle(request);
handle() returns null for non-docs requests so it can run in Farm's request
pipeline without taking over application routes. The server also exposes
load, GET, POST, and MCP for custom routing.
Entrypoints
| Entrypoint | Purpose |
|---|---|
@farming-labs/farmjs/config | Farm config wrapper |
@farming-labs/farmjs/server | Page loader and API, agent, and MCP request handlers |
@farming-labs/farmjs/react | React documentation page renderer |
@farming-labs/farmjs/content | Content and navigation utilities |
@farming-labs/farmjs/vite | MDX compilation plugin |
@farming-labs/farmjs/runtime | Versioned Farm runtime contract |
@farming-labs/farmjs/edge-compiler | Build-time edge snapshot compiler used by Farm |
@farming-labs/farmjs/api-reference | Farm API-reference handler |
License
MIT