Astroでzenn-markdownを使う
はじめに
zenn-markdownとは、普段のZennで書いたり見たりする記事のように素のMarkdownをいい感じの見た目にえてくれる、いわゆるMarkdownパーサーというものです。
最近個人的にAstroでブログを作成しており、Markdownパーサーをどうしようかと考えていたのですが、
以前Next.jsでブログを作成したときに使ったzenn-markdownがとても簡単で良かったので、Astroでも使いたいなと思い導入しました。
しかし、Astro側の設定と嚙み合わないところが多く完全に機能するまでに結構つまづいたので、同じ人が出ないようにここに記しておきたいと思います。
皆さんの参考になれば幸いです!
導入方法
基本的に以下で書かれていることと同じ手順で同じことをすれば問題ないです。
zenn-markdown-htmlとzenn-markdown-cssをインストールする
1.一番最新のパッケージをインストールします。
今あるのは 2023/02/21時点 でバージョンが0.1.140なので、以下のようにpackage.jsonに追記します。
"dependencies": {
...
+ "zenn-content-css": "^0.1.140",
+ "zenn-embed-elements": "^0.1.140",
+ "zenn-markdown-html": "^0.1.140"
}
その後、yarn install
を実行してパッケージを取り込みます。
zenn-markdown-htmlを使う
2.zenn-markdown-htmlとは、素のMarkdownをHTML文字列に変換するものです。
基本的な使い方としては以下のようになります。
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
というエラーが発生してしまうので以下のように直します。
- 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
について
Markdownの中身を<Fragment set:html={content} />
とすることで表示できます。
...
// Markdownの中身を表示させたい場所に以下を追加
+ <Fragment set:html={content} />
...
zenn-content-cssを使う
4.このままではHTMLに何もcssが当たっていない状態なのでzenn-content-css
を使ってデザインを適用するようにします。
...
// 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
について
<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