🍊

【Deno】準備編:MarkdocとFreshでドキュメントサイトを作る

2022/10/04に公開

MarkdocとFreshを使ってドキュメントサイトのテンプレートを作ろうとしている記事です。

MarkdocとFreshについては、以前記事を書きましたので良ければ読んでいただけると嬉しいです。
https://zenn.dev/k41531/articles/8e2d7244a5e157
https://zenn.dev/k41531/articles/69c9342f7022dd

この二つの技術を選んだことに大きな理由はないのですが、強いて述べるとすれば、最近denoがnpmに対応したとのことで、そうであればdenoとMarkdocの組み合わせもできるかと思い試してみました。

とりあえず動かす

freshのプロジェクトを作る

deno run -A -r https://fresh.deno.dev doc-site

Markdocを読み込む

denoではパッケージをimport_map.jsonで管理しているので、そこにmarkdocを追記します。

"markdoc": "npm:@markdoc/markdoc"

MarkdownをMarkdocで表示する

コンポーネントを作る

Makdownを表示するための、Pageというコンポーネントをcomponents/Page.tsxに作ります。
注意点として、freshはpreactを使っているので、import React from "preact/compat";でReactにレンダリングできるようにします。

import Markdoc from "markdoc";
import React from "preact/compat";

export function Page() {
  const doc = `
  # Hello world.
  > My first Markdoc page
  `;

  const ast = Markdoc.parse(doc);

  const content = Markdoc.transform(ast);

  const html = Markdoc.renderers.react(content, React);  
  return html;
}

コンポーネントを表示

route/index.tsxを書き換えて、Pageコンポーネントを表示します。

import { Page } from "../components/Paper.tsx";

export default function Home() { 
  return (
    <div>
      <Paper />
    </div>
  );
}

コマンドの書き換え

最後に、denoのnpmサポートはまだ実験段階なので、denoで動かすときには特別なフラグ--unstableが必要です。そのため、deno.jsonを書き換えます。

{
  "tasks": {
    "start": "deno run --unstable -A --watch=static/,routes/ dev.ts"
  },
  "importMap": "./import_map.json",
  "compilerOptions": {
    "jsx": "react-jsx",
    "jsxImportSource": "preact"
  }
}

それでは、実行。

deno task start

Markdocをあっさりと読み込み、特に問題なく動いたので驚きました。denoすごい。

...と思ったのですが、deno deployにデプロイしようすると次のようなエラーが出て失敗。デプロイする方法は模索中です。
冒頭でも述べた通り、esm.shを使えばDeno Deployにデプロイしても問題なく使えます。

Error The deployment failed: Module not found "npm:@markdoc/markdoc".

ファイルを読み込む

Markdownを直書きしているので、ファイルを読み込むようにします。
static/docs/markdown.mdを追加。

markdown.md
# Headers

**Bold**

_Italic_

[Links](/docs/nodes)

![Images](/logo.svg)

Lists
- Item 1
- Item 1
- Item 1

> Quotes
`Inline code`

Code fences


pageコンポーネントが引数を受け取るように書き換える。


import Markdoc from "markdoc";
import React from "preact/compat";

export function Page(props: { doc:string }) {
  console.log(props.doc.markdown);
  const ast = Markdoc.parse(props.doc);
  
  const content = Markdoc.transform(ast);
  
  const html = Markdoc.renderers.react(content, React);  
  return html;
}

Handlerを使って、GETアクセスが来たらファイルを読み込んでコンポーネントに渡すようにする。

import { Handlers, PageProps } from "$fresh/server.ts";
import { Page } from "../components/Page.tsx";

interface Data {
  markdown: string;
}

export const handler: Handlers = {
  async GET(_req, ctx) {
    const url = new URL(`../static/docs/markdown.md`, import.meta.url);
    const fileContent = await Deno.readTextFile(url).catch( _ => {
      return "Page Not Found"
    });

    const page = {markdown : fileContent}
    const resp = ctx.render(page);
    return resp;
  },
};

export default function Home(props: PageProps<Data> ) {
  return (
    <div>
      <Page doc={props.data.markdown} />
    </div>
    );
}

ここまでで、基本的な準備が完了したので、あとはルーティングやサイドバー、ヘッダーを作成していきます(未完成)
デプロイが出来たら現時点でのサイトのリンクを貼りたかったのですが、上手くいっていないのでスクショだけ貼っておきます。
https://markdoc-fresh.deno.dev/home
デザインは完全にFreshのドキュメントを真似ています。

また、今回の内容はMandarinという名前でリポジトリを作っています。
もし、この組み合わせでやってみたい方がいらしたら参考になるかも?
https://github.com/k41531/markdoc-fresh

Discussion