469文字
2分
編集

AstroでMarkdownのレンダリングにカスタムコンポーネントを利用する

#MDX のカスタムコンポーネント

Astro では、MDX 用にカスタムコンポーネントを利用できる機能が提供されている。

astro
---
import Heading from "../Heading.astro";
---

<Content components={{ h1: Heading }} />

https://docs.astro.build/en/guides/markdown-content/#custom-components-with-imported-mdx

#Markdown 用には同様の機能がない

利用方法がほとんど同じだが、マークダウンには同様の機能がない。拡張子を mdx に変更すれば、そのまま利用することは出来るが、それが望ましいとは限らない。

https://github.com/withastro/roadmap/discussions/769

https://github.com/withastro/roadmap/discussions/423

公式ドキュメントには MDX では出来るとは書かれているが、Markdown では出来ないとは書かれていないため、誤認したと思われる人による報告が見られる。

#Remark plugin を利用する

Gatsby や Next.js などの他のフレームワークと同様に、remark plugin を利用して要素をカスタマイズすることは出来る。 一応 Astro 公式でも、カスタムコンポーネントを使う文脈ではないが remark plugin を利用する方法は紹介されている。

https://docs.astro.build/recipes/reading-time/

Astro のサンプルは、frontmatter を拡張するものだが、要素の変換も次のように行うことが出来る。

javascript
import { visit } from "unist-util-visit";
import { toString } from "mdast-util-to-string";

const remarkPluginSample = () => {
  const transformer = (tree: any) => {
    visit(tree, (node) => {
      if (node.type !== "heading") return;
      let { depth } = node;
      if (depth !== 1) return;

      let text = toString(node);
      const html = `<h1 style="color: coral">${text}</h1>`;

      node.type = "html";
      node.children = undefined;
      node.value = html;
    });
  };

  return transformer;
};

export default defineConfig({
  markdown: {
    remarkPlugins: [remarkPluginSample],
  },
});

#余談:公式 MDX Integration の拡張を考える

@astrojs/mdxのコードをざっくり見ると、内部 API の addPageExtension.mdも追加すれば対応できるように思える。試してみて問題がなさそうであれば、PR を送ってみてもいいかもしれない。

個人的には Markdown の場合は既存のエコシステムを使うで十分だと思うので、今回は試さなかった。

編集