【Astro】rehype (remark) プラグインを自作してMarkdown内の内部リンクをカードに変換する
AstroでMarkdown・MDXを扱う際、remark及びrehypeのサードパーティプラグイン1のお世話になることが多いが、今回は自前で簡易的なrehypeプラグインを作ってみる。
やりたいこと
mdファイル内に埋め込んだ内部リンク (自サイトの別記事へのテキストリンク) をリンクカードに変換したい2。
[](/some-page)上記のように/から始まる相対パスの場合に内部リンクとみなす。
<a href="/some-page" class="internal-link">  <span>title</span>  <time datetime="2000-01-01T00:00:00.000Z">2000-01-01</time></a><a>には任意のクラスを指定し、タイトルと投稿日をリンク内に挿入する。
解決法
1. rehypeプラグインを実装
npm i unist-util-visithastをwalkするために、unistのユーティリティモジュールunist-util-visitをインストールする。
import type { ElementContent, Root } from 'hast';import { visit } from 'unist-util-visit';
export default function rehypeInternalLink() {  return (tree: Root) => {    visit(tree, 'element', (node) => {      if (node.tagName !== 'a') return;      const href = node.properties.href;      if (typeof href !== 'string' || !href.startsWith('/')) return;
      const { title, createdAt } = getPost(href); // 何らかの方法で投稿情報を取得
      const titleElm = {        type: 'element',        tagName: 'span',        children: [          {            type: 'text',            value: title,          },        ],      } satisfies ElementContent;      const timeElm = {        type: 'element',        tagName: 'time',        properties: { dateTime: createdAt.toJSON() },        children: [          {            type: 'text',            value: formatDate(createdAt),          },        ],      } satisfies ElementContent;
      node.children = [titleElm, timeElm];      node.properties.className = 'internal-link';    });  };}hast (Node) をvisitし、<a>かつhrefが相対パスであれば任意のElementを挿入する3。
import type { ElementContent, Root } from 'hast';import { visit } from 'unist-util-visit';import styles from './index.module.css';
export default function rehypeInternalLink() {  return (tree: Root) => {    visit(tree, 'element', (node) => {      ...
      node.children = [titleElm, timeElm];      node.properties.className = 'internal-link';      node.properties.className = styles.card;    });  };}もしCSS Modulesを利用している場合はclassNameに直接指定しスタイリングすることもできる。
2. 設定ファイルに追記
import rehypeInternalLink from './rehype-internal-link';
export default defineConfig({  markdown: {    rehypePlugins: [rehypeInternalLink],  },});あとはAstroの設定ファイルのrehypePluginsに指定すれば完成。リンクカードに変換されるようになった。
参考
- 
remark/doc/plugins.md・rehype/doc/plugins.mdから一覧を参照できる ↩
 - 
外部リンクをリンクカードに変換する場合はremark-link-cardを利用するのが楽そう。OGP画像も表示してくれる ↩