👩‍🚀

Astroでzenn-markdownを使う

2023/02/22に公開

はじめに

zenn-markdownとは、普段のZennで書いたり見たりする記事のように素のMarkdownをいい感じの見た目にえてくれる、いわゆるMarkdownパーサーというものです。

最近個人的にAstroでブログを作成しており、Markdownパーサーをどうしようかと考えていたのですが、
以前Next.jsでブログを作成したときに使ったzenn-markdownがとても簡単で良かったので、Astroでも使いたいなと思い導入しました。

しかし、Astro側の設定と嚙み合わないところが多く完全に機能するまでに結構つまづいたので、同じ人が出ないようにここに記しておきたいと思います。

皆さんの参考になれば幸いです!

導入方法

基本的に以下で書かれていることと同じ手順で同じことをすれば問題ないです。
https://zenn.dev/team_zenn/articles/intro-zenn-markdown

1. zenn-markdown-htmlzenn-markdown-cssをインストールする

一番最新のパッケージをインストールします。
今あるのは 2023/02/21時点 でバージョンが0.1.140なので、以下のようにpackage.jsonに追記します。

package.json
  "dependencies": {
     ...
+    "zenn-content-css": "^0.1.140",
+    "zenn-embed-elements": "^0.1.140",
+    "zenn-markdown-html": "^0.1.140"
  }

その後、yarn installを実行してパッケージを取り込みます。

2. zenn-markdown-htmlを使う

zenn-markdown-htmlとは、素のMarkdownをHTML文字列に変換するものです。

基本的な使い方としては以下のようになります。

[...slug].astro
import markdownToHtml from "zenn-markdown-html";

// markdownToHtml関数に素のMarkdown(=post.body)を渡してHTML(=content)として受け取る
const content = markdownToHtml(post.body, {
  embedOrigin: "https://embed.zenn.studio",
});

しかし、Astroの場合このままビルドするとmarkdownToHtml is not a functionというエラーが発生してしまうので以下のように直します。

[...slug].astro
- import markdownToHtml from "zenn-markdown-html";
+ import lib from "zenn-markdown-html";

+ // build時にそのまま使うとエラーになるため修正
+ const markdownToHtml = lib.default ? lib.default : lib;

// markdownToHtml関数に素のMarkdown(=post.body)を渡してHTML(=content)として受け取る
const content = markdownToHtml(post.body);

これは暫定的な対応で、VSCode上だと赤い波線が出てしまうのですが機能はするのでこのままで良しとします。

どうしてビルドエラーになるのか

実行コマンドによってmarkdownToHtmlのimport結果が変わるためmarkdownToHtml is not a functionというエラーが発生してしまうようでした。

私自身も最初どうしてエラーになってしまうのか分からなかったのでZennの開発者さんに聞いたところ、

こちらで確認したところ、markdownToHtml is not a function ビルドエラーになってしまうのは build コマンドを実行すると、import 結果が変わってしまうことが原因でした。具体的には default import の値が Function から { defualt: Function } になってしまいます👇

import lib from "zenn-markdown-html";
console.log( lib ); 
// yarn dev の場合だと `Function`
// yarn build の場合だと `{ default: Function }` になる

とのことで、実際に確認しても上記の通りになっていました。
今後Astro側が解決してくれればいいのですが、いつになるか分からないので暫定的な対応を取るしかなさそうです。

3. HTML文字列をHTMLに変換して表示する

2で受け取ったcontentはただのHTML文字列なので、HTML要素として認識してもらうための処理が必要になります。

Astroでは、set:htmlというものがあります。(ReactでいうdangerouslySetInnerHTML)

set:htmlについて
https://docs.astro.build/ja/reference/directives-reference/#sethtml

Markdownの中身を<Fragment set:html={content} />とすることで表示できます。

[...slug].astro
...

// Markdownの中身を表示させたい場所に以下を追加
+ <Fragment set:html={content} />

...

4. zenn-content-cssを使う

このままではHTMLに何もcssが当たっていない状態なのでzenn-content-cssを使ってデザインを適用するようにします。

[...slug].astro
...
// importを追加
+ import 'zenn-content-css';

+ <div class="znc">
    <Fragment set:html={content} />
+ </div>
...

5. 埋め込みコンテンツを有効にするためのscriptを追加する

埋め込みコンテンツの表示で必要なscriptを<head>タグ内で追加します。

ここでつまづきポイントなのですが、Astroではデフォルトで<script><style>を最適化する処理を行っているため単純に<script src="https://embed.zenn.studio/js/listen-embed-event.js"を追加するだけではブラウザ上でCORSエラーが発生します。

そのため、そのデフォルト処理を行わないようにするために<script>内にis:inlineを追加する必要があります。※参考

is:inlineについて
https://docs.astro.build/ja/reference/directives-reference/#isinline

.astro
<head>
  <!-- Astroのscript最適化処理をキャンセルするためにis:inlineを追加 -->
  <script is:inline src="https://embed.zenn.studio/js/listen-embed-event.js"
  ></script>
</head>

これで正常に表示が出来るようになりました!

おわりに

Astroでzenn-markdownを導入しました。Next.jsとは違う挙動がありエラーが発生するところが多かったので苦戦しましたが、同じくAstroでzenn-markdownを導入しようとしている人の助けになっていれば嬉しいです🐸

Discussion