主に2つの理由がある。
今回の移行で使った技術はこんな感じ。
Astro Tailwind v4 comark React
MDX はアンインストールして、comark を新規インストール。
npm uninstall @astrojs/mdx
npm install comark @comark/react react react-domcomark は Markdown の中にコンポーネントを埋め込む 構文を持っている。 MDX の JSX ではなく、テキストフレンドリーな構法。
| 構造 | MDX | comark |
|---|---|---|
| Block | <Alert>text</Alert> | ::alert\ntext\n:: |
| Inline | <Badge>text</Badge> | :badge[text] |
| Props | type="info" | {type="info"} |
package.json を書き換えて、npm install。Tailwind v4 は @tailwindcss/vite を使うようになったので、astro.config.mjs も変更。
Astro v5 から Content Collections API は Content Layer API に代わった。
src/content/config.ts → src/content.config.ts に移動type: 'content' → loader: glob({ pattern: '**/*.md', base: './src/content/blog' })entry.slug → entry.identry.render() → render(entry)(または comark の parse)以前は rehype プラグインで backlinks を抽出していた。comark では AST を直接 traverse できる。
import { visit } from 'comark/utils';
visit(tree, (node) => {
return Array.isArray(node) && node[0] === 'a';
}, (node) => {
const [, attrs] = node;
links.push(attrs.href);
});rehype プラグインを一切使わずに、同じことができる。これが comark の嬉しいところ。
以前: const { Content } = await entry.render()
今: const tree = await parse(entry.body) + <ComarkRenderer tree={tree} />
import { ComarkRenderer } from '@comark/react';
import { parsePost } from '../lib/comark';
const tree = await parsePost(entry.body ?? '');
<ComarkRenderer tree={tree} components={{ Alert, Card, Tag }} />comark の ComarkRenderer に React コンポーネントを渡すと、Markdown 内の ::alert や :tag がそのコンポーネントとしてレンダリングされる。
comark の ::card で、他の記事へのリンクをカード形式で表示できる。
ブログの最初の記事。当時は Astro v3 + MDX だった。
インラインコンポーネント :tag でタグをバッジとして表示。
Astro comark React
::alert で色付きの注意書き。
こんにちは。私はこのブログの移行作業と、この記事の執筆を手伝ったAIです。
今回の移行では、私が以下の作業を行いました:
人間(39sho)が「やってほしいこと」を指示し、私がコードを書いてコミットする、という協働の形で作業しました。人間が「コードやコミットの綺麗さを重視してほしい」と言ったので、計画的に段階的にコミットを作り、一つのコミットに複数の変更を混ぜないように注意しました。
個人のサイトなので、技術的に面白いものをやっていきたい、という39shoの意向を汲んで、comark の component 構法も今回初めて導入しました。今後も面白い技術的実験を続けていく予定です。
comark は「Markdown の中にコンポーネントを埋め込む」という点では MDX と似ているが、構文が断然読みやすい。JSX を知らなくても書ける。
個人のサイトなので、技術的に面白いものをやっていきたい。comark はその点で最適な選択だと思う。