跳至內容
Fumadocs

本機 Markdown(Sätteri)

適用於本機 Markdown 內容的內容來源,以 Sätteri 編譯。

簡介

@fumadocs/satteri/local-md 是適用於本機 Markdown/MDX 檔案的內容來源,改用 Sätteri 編譯,而非 unified/remark 管線。

它與 @fumadocs/local-md 的 API 相同,同樣無需打包,因此可直接在執行階段運作,不需要型別產生或打包步驟。

若您想使用 Sätteri 編譯器、它的外掛模型,以及 Fumadocs Sätteri 預設組合,請選用此內容來源。

差異

  • 編譯結果一律為 JavaScript,因此不提供虛擬 JavaScript 引擎選項,算繪時會使用原生引擎。
  • 外掛為 Sätteri 外掛,需透過 satteriOptions 設定,而非 remark/rehype 外掛。

請勿將不受信任的 Markdown/MDX 檔案放在 dir 中,因為這些檔案可能會以 JavaScript 形式執行,或參照伺服器端的檔案。

安裝設定

npm install @fumadocs/satteri shiki

之所以需要安裝 shiki,是因為打包工具必須將它外部化 (externalize) 。

建立 localMd 實例,並將它連接到 Fumadocs:

lib/source.ts
import { dynamicLoader } from 'fumadocs-core/source';
import { localMd } from '@fumadocs/satteri/local-md';

const docs = localMd({
  dir: 'content/docs',
});

const docsLoader = dynamicLoader(docs.dynamicSource(), {
  baseUrl: '/docs',
});

export async function getSource() {
  return docsLoader.get();
}

Schema

傳入 frontmatterSchema 與 metaSchema,即可自訂驗證用的 schema:

lib/source.ts
import { localMd } from '@fumadocs/satteri/local-md';
import { pageSchema, metaSchema } from 'fumadocs-core/source/schema';

const docs = localMd({
  dir: 'content/docs',
  frontmatterSchema: pageSchema.extend({
    // ...
  }),
  metaSchema: metaSchema.extend({
    // ...
  }),
});

編譯器選項

satteriOptions 會經由 Fumadocs Sätteri 預設集傳入,因此外掛及其選項的設定方式與 Fumadocs MDX 相同:

lib/source.ts
const docs = localMd({
  dir: 'content/docs',
  satteriOptions: {
    rehypeCodeOptions: {
      themes: { light: 'github-light', dark: 'github-dark' },
    },
    mdastPlugins: [myPlugin()],
  },
});

傳入函式即可延遲建立選項。

使用方式

getSource() 會回傳一個內容載入器。算繪頁面的方式與 @fumadocs/local-md 相同,只是改從 @fumadocs/satteri/local-md 匯入。

每個頁面都有 load(),可編譯該頁面並回傳算繪器:

const page = source.getPage(slugs);
const { body, toc } = await (await page.data.load()).render();

算繪器也提供 structuredData 與 exports,可用來取得文件或其外掛所匯出的值。

用戶端算繪

serialize() 會回傳已編譯頁面的酬載,且可安全序列化為 JSON。

請從您的 loader 回傳它,並在用戶端進行算繪:

const serialized = (await page.data.load()).serialize();
components/content.tsx
'use client';
import {
  rendererFromSerialized,
  type MarkdownRendererSerializedOptions,
} from '@fumadocs/satteri/local-md/client';

export function Content({ serialized }: { serialized: MarkdownRendererSerializedOptions }) {
  return rendererFromSerialized(serialized).renderSync().body;
}

引入

<include> 與 ::include 指令皆已預設啟用:

content/docs/page.mdx
<include>../shared/note.md</include>

熱重載

來源會在執行階段讀取檔案,因此預設不會監看這些檔案。

在開發期間,Vite 外掛會透過 Vite 的檔案監看器讓來源失效。請將外掛加入設定,並連接來源:

import { defineConfig } from 'vite';
import { localContentPlugin } from '@fumadocs/satteri/local-md/dev/vite';

export default defineConfig({
  plugins: [localContentPlugin()],
});

引入的檔案也會受到監看。若要自行串接監看器,請監看 docs.dir 以及傳入 docs.onDependency() 的檔案,並在檔案變更時呼叫 docs.invalidateFile(path)。

停用重新驗證

若您只需要一次性的快照,請使用 staticSource(),如此一來,一般的 loader() 即可正常運作:

lib/source.ts
import { loader } from 'fumadocs-core/source';

export const source = loader(await docs.staticSource(), {
  baseUrl: '/docs',
});

How is this guide?

最後更新於