コンテンツへスキップ
Fumadocs

ローカル HTML

ローカルの HTML ファイルを扱うコンテンツソースです。

はじめに

@fumadocs/local-html は、ローカルの HTML ファイル用のコンテンツソースです。バンドラーを使わない設計のため、完全にランタイムで動作します。

自分で執筆するのではなく、HTML として受け取るページを対象としています。たとえば、エクスポートしたスライドやレポート、他のツールの出力などです。各ファイルはそのままの形でレンダリングされるのではなく、ドキュメントのテーマに合わせて変換されます。

ローカル Markdown とは異なり、HTML は執筆に使うドキュメント形式ではなく、変換対象のデータとして扱われます。

制限事項

  • スクリプトは除去されるため、元のページのインタラクティブな動作は再現されません。
  • コンテンツ内で MDX コンポーネントは使用できませんが、レンダリング時に HTML タグをコンポーネントにマッピングできます。
  • この処理は変換であり、サニタイズではありません。実行されるものは一切残りませんが、残ったマークアップはそのままレンダリングされ、そこで参照されているリソース (特に画像) は、ページを開いたすべてのユーザーの環境で読み込まれます。コンテンツディレクトリには、Markdown コンテンツなど、信頼できるファイルだけを置いてください。

セットアップ

パッケージをインストールします。

npm install @fumadocs/local-html

localHtml インスタンスを作成し、Fumadocs に接続します。

lib/source.ts
import { dynamicLoader } from 'fumadocs-core/source';
import { localHtml } from '@fumadocs/local-html';

const pages = localHtml({
  dir: 'content/pages',
  // オプション
});

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

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

pagesLoader.get() から返されるソースは、通常のコンテンツローダーのインスタンスです。

使い方

page.data.load() は、処理済みコンテンツのレンダラーを返します。

ページ内でレンダリングします。

import type { ReactNode } from 'react';
import { DocsLayout } from 'fumadocs-ui/layouts/docs';
import { getSource } from '@/lib/source';

export default async function Layout({ children }: { children: ReactNode }) {
  const source = await getSource();

  return <DocsLayout tree={source.getPageTree()}>{children}</DocsLayout>;
}

render() にコンポーネントを渡すと、HTML タグをアンカー付きの見出しなどの独自コンポーネントに置き換えられます。

import { getMDXComponents } from '@/components/mdx';

const { body, toc } = await renderer.render(getMDXComponents());

ページのメタデータ

各ページのタイトルと説明は、そのページの <head> から読み取られます。

  • <title> がページのタイトルになります。
  • <meta name="description"> が説明になります。

fumadocs: の meta タグを使用すると、表示されるコンテンツを変更せずにこれらを上書きできます (アイコンの設定も可能です) 。

<meta name="fumadocs:title" content="My Page" />
<meta name="fumadocs:description" content="A short description." />
<meta name="fumadocs:icon" content="SquareBookmark" />

ドキュメント内のすべての <meta name>/content のペアは、page.data.metadata からも参照できます。

他のローカルソースと同様に、meta.json ファイルでページツリーをカスタマイズでき、その内容は metaSchema で検証されます。

コンテンツオプション

ページからはコンテンツ要素のみが抽出されます。唯一の <main> があればそれを、なければ唯一の <article> を使用し、どちらもない場合は <header> や <nav> などのページ装飾部分を取り除いた <body> にフォールバックします。テーマの本文用スタイル (prose) が適用されるよう、この要素の class 属性と style 属性は削除されます。また、目次と検索に利用できるよう、見出しには id が付与されます。

これらの動作はそれぞれ設定で変更できます。

import { localHtml } from '@fumadocs/local-html';

const pages = localHtml({
  dir: 'content/pages',
  // テーマに合わせて調整せず、元の class/style 属性を保持する
  adaptStyles: false,
  // 指定したタグもコンテンツから除外する
  exclude: ['aside'],
  // ページコンテンツを含む要素を選ぶカスタムロジック
  selectContent: (root) => {
    // hast 要素を返す(デフォルトの動作にする場合は undefined を返す)
  },
});

シンタックスハイライト

<pre><code> ブロックは Shiki によってハイライトされます。オプションは ローカル Markdown と同じものが使用されます。

const pages = localHtml({
  dir: 'content/pages',
  rehypeCodeOptions: {
    themes: {
      light: 'github-light',
      dark: 'github-dark',
    },
  },
});

言語は、ブロックの <code> 要素、またはそれを囲む <pre> 要素に付与された language-* クラスから判別されます。

<pre><code class="language-ts">const answer: number = 42;</code></pre>

言語が指定されていないブロックは、プレーンテキストとして表示されます。シンタックスハイライトを無効にするには、rehypeCodeOptions: false を渡してください。

ホットリロード

@fumadocs/local-html はランタイムにファイルを読み込むため、デフォルトではファイルの変更を監視しません。

開発中は、Vite プラグインが Vite のファイルウォッチャーを使ってソースを無効化するため、追加のプロセスやポートは必要ありません。設定ファイルにプラグインを追加し、ソースを接続してください。

import { defineConfig } from 'waku/config';
import { localContentPlugin } from '@fumadocs/local-html/dev/vite';

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

独自のウォッチャーを使う場合は、ファイルの変更時に pages.invalidateFile(path) を呼び出してください。

再検証を無効にする

再検証が不要で、一度きりのスナップショットだけが必要な場合は、staticSource() を使用できます。

dynamicLoader() だけでなく、通常の loader() でも使用できます。

lib/source.ts
import { loader } from 'fumadocs-core/source';
import { localHtml } from '@fumadocs/local-html';

const pages = localHtml({
  dir: 'content/pages',
});

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

How is this guide?

最終更新日