remark-gfmの脚注内リンクを処理するremarkプラグインの作り方

投稿日
2025年1月12日
remark-gfmの脚注内リンクを処理するremarkプラグインの作り方の見出し画像
目次

はじめに

Astroではremark-gfm(GitHub風マークダウンを実現するremark)がデフォルトで有効です。そのため、astro.config.tsremarkPluginsで渡されるmdastは、すでにremark-gfmで処理されています。

AstroはGitHub風のマークダウンをデフォルトで使用します。

docs.astro.buildファビコン 設定方法
docs.astro.build
astro
5.1.1

remark-gfmで生成される脚注ノード

remark-gfmでは下記のようなMarkdownで脚注を記述できます。

sample[^1]なんだよ
[^1]: https://example.com/

得られるmdastは下記です。ちなみに、こんな感じでunified系のASTをツリー表示したい場合はunist-util-inspectライブラリが非常に便利です。

root[2] (1:1-5:1, 0-44)
├─0 paragraph[3] (2:1-2:15, 1-15)
│ ├─0 text "sample" (2:1-2:7, 1-7)
│ ├─1 footnoteReference (2:7-2:11, 7-11)
│ │ identifier: "1"
│ │ label: "1"
│ └─2 text "なんだよ" (2:11-2:15, 11-15)
└─1 footnoteDefinition[1] (4:1-4:27, 17-43)
│ identifier: "1"
│ label: "1"
└─0 paragraph[1] (4:7-4:27, 23-43)
└─0 link[1] (4:7-4:27, 23-43)
│ title: null
│ url: "https://example.com/"
└─0 text "https://example.com/" (4:7-4:27, 23-43)

ここから最終的に脚注セクションとしてHTMLに現れるわけですが、footnoteDefinition以下のlinkノードのみ処理するremarkプラグインをどう作るかというのが表題の意です。

脚注内リンクノードの検知

tree走査用のライブラリとしてunist-util-visit-parentsを使います。 unist-util-visitとは違い、第三引数のvisitorメソッドで親ノードではなく祖先ノードリストを受け取ることができます。

unist-util-visitunist-util-visit-parentsvisitメソッドの第二引数はNodeタイプのマッチング条件です。linkを指定したことでLinkノードのみを処理することを意味します。

さらにvisitor側で祖先ノードにfootnoteDefinitionが存在しない場合にreturnするようにすれば、脚注内以外のLinkノードを除外できます。

import { type Plugin } from 'unified';
import { visitParents } from 'unist-util-visit-parents';
import type { Root } from 'mdast';
export const remarkLinkCard: Plugin<[], Root> = () => {
return (tree) => {
visitParents(tree, 'link', (node, ancestors) => {
if (!ancestors.some((ancestor) => ancestor.type === 'footnoteDefinition'))
return;
// 脚注内のリンクノードに対する処理
});
};
};

おわりに

unist-util-visitfootnoteDefinitiontestに指定して、そのノードのchildrenに含まれるLinkノードという順でも実装可能ですね。 でも、コードがちょっと煩雑になりそうな気がする🤔

以上、忘れないようにメモでした。

関連記事