needhelp
← ブログに戻る

Astro 6.4 深層解説:プラグイン可能な Markdown パイプライン、Rust 駆動の Sätteri、そして Cloudflare デプロイ革命

著者 needhelp
Astro
フロントエンド
Rust
Cloudflare
Markdown
SSG
Web 開発

2026 年 5 月 28 日、Astro は 6.4 をリリースした。これは通常の機能追加でも単なるバグフィックスでもない——構造的な転換点だ。

3 つの核心的な変更は、それぞれが深いトレンドラインを切り開いている:

  • Markdown プロセッサのインターフェース化——unified による 10 年の独占に別れを告げる
  • Sätteri——Rust でゼロから書かれた Markdown/MDX プロセッサ。CI ビルド時間を 120 秒から 55 秒に短縮
  • cf() ヘルパー関数——Cloudflare 上の 6 つ以上のバインディングとコンテキスト注入を 1 行に圧縮

以下で詳しく見ていく。


一、Unified 独占の終焉

1.1 歴史的経緯

Astro は誕生当初から Markdown パイプラインを unified エコシステムに強く依存していた——具体的には remark(Markdown AST のパース)+ rehype(HTML AST への変換)とその数千にのぼるプラグインだ。それ自体は問題ではない——unified のエコシステムは巨大で柔軟性がある。問題はそれがハードコードされていたことだ。

差し替えが効かない。あなたのユースケースが GFMD と見出しアンカーだけだとしても、remark → rehype → stringify という JS パイプライン全体が必ず実行される。

6.4 の markdown.processor API は、このパイプラインを固定依存から交換可能なインターフェースへと変えた。

1.2 アーキテクチャの変化

graph TD
    subgraph "6.4 以前:ハードコードされたパイプライン"
        A1[astro.config] -->|固定| B1[Unified エンジン]
        B1 --> C1[remarkPlugins]
        B1 --> D1[rehypePlugins]
        C1 --> E1[Markdown AST]
        D1 --> E1
    end

    subgraph "6.4+:プラグイン可能なパイプライン"
        A2[astro.config] -->|markdown.processor| B2[Processor インターフェース]
        B2 --> C2[Unified<br/>デフォルトプロセッサ]
        B2 --> D2[Sätteri<br/>Rust プロセッサ]
        B2 --> E2[カスタムエンジン]
        C2 --> F2[JS プラグインエコシステム]
        D2 --> G2[ネイティブ Rust パイプライン]
        E2 --> H2[ユーザー定義の AST]
    end

    style A1 fill:#ffcccc
    style A2 fill:#ccffcc
    style B2 fill:#e1f5fe

核心的な変化は次の点にある:astro.config はもはや remarkPlugins / rehypePlugins といったトップレベル設定を直接受け付けない。代わりに統一された processor() 呼び出しを用いる。

1.3 新しい設定の書き方

古い書き方は 6.4 でもまだ動作するが、すでに非推奨とされており、Astro 8.0 で削除される:

// ❌ 非推奨(6.4 では互換性維持、8.0 で削除)
import { defineConfig } from 'astro/config';
export default defineConfig({
markdown: {
remarkPlugins: ['remark-toc'],
rehypePlugins: ['rehype-slug'],
smartypants: true,
gfm: true,
},
});

新しい書き方:

// ✅ Astro 6.4+ 推奨
import { defineConfig } from 'astro/config';
import { unified } from '@astrojs/markdown-remark';
import remarkToc from 'remark-toc';
import rehypeSlug from 'rehype-slug';
export default defineConfig({
markdown: {
processor: unified({
remarkPlugins: [remarkToc],
rehypePlugins: [rehypeSlug],
smartypants: true,
gfm: true,
}),
},
});

変更は小さいが、アーキテクチャ上の意味は大きい——すべての Markdown 設定が 1 つの processor 呼び出しに集約され、いつでも Sätteri や他のエンジンに丸ごと差し替えられるようになった。

1.4 非推奨のタイムライン

6.4 から 8.0 までのウィンドウは約 12〜18 ヶ月。移行が遅れれば遅れるほど、アップグレードの痛みは大きくなる:

[ \text{技術的負債リスク} = \int_{t_{6.4}}^{t_{8.0}} \text{設定膨張度}(t) , dt ]

新しいプラグインと新しいページを同時に追加しているプロジェクトでは、累積負債は超線形的に増加する。今すぐ整理を始めることを推奨する。


二、Sätteri:Rust が Markdown パイプラインに参入

2.1 それは何か

@astrojs/markdown-sätteriゼロから書き直された Rust 製 Markdown/MDX プロセッサだ。これは unified の Rust 高速版ではない——独自の AST 仕様、独自のパーサー、独自のシリアライザーを持つ。つまり remark プラグインをより速く動かすのではなく、remark プラグインをそもそも動かさない。

2.2 パフォーマンス実測

Astro チームは 2 つの実在サイトでベンチマークを実施した:

サイトUnified (ベースライン)Sätteri高速化倍率
Astro 公式ドキュメントサイト142s63s2.25×
Cloudflare ドキュメントサイト120s55s2.18×
中規模マーケティングサイト38s22s1.73×

Sätteri の高速化は大規模ドキュメントサイトで最も顕著だ。理由は明白——unified パイプラインでは各プラグインが AST を丸ごと走査し、プラグインが増えるほど走査回数が増える。Sätteri は一般的な GFM 機能(テーブル、タスクリスト、自動リンク、取り消し線)をコンパイル時オプションとして持ち、1 回の走査で完了する:

xychart-beta
    title "ビルド時間比較:Unified vs Sätteri"
    x-axis ["Unified (ベースライン)", "Sätteri (Rust)"]
    y-axis "ビルド時間 (秒)" 0 --> 150
    bar [120, 55]

CI/CD シナリオにおける累積削減量は次のように計算できる:

[ \text{総削減量} = n_{\text{日次ビルド回数}} \times \Delta T \times d_{\text{稼働日}} ]

日次 50 回のビルド × 毎回 65 秒 = 1 日あたり 54 分節約。年間約 230 時間の CI 時間削減となる。

2.3 ただし互換性が課題

Sätteri は remark/rehype プラグインと互換性がない。これはバグではなく、Rust AST パイプラインのアーキテクチャ上の必然だ。MDAST(Markdown AST)と HAST(HTML AST)は JavaScript のデータ構造であり、Rust ネイティブパイプラインは JS プラグインを直接実行できない:

graph LR
    subgraph "プラグイン互換性マトリックス"
        direction TB
        P1[remark-toc] -->|❌ 非対応| S[Sätteri]
        P2[remark-gfm] -->|✅ ネイティブ対応| S
        P3[rehype-slug] -->|❌ 非対応| S
        P4[rehype-autolink-headings] -->|❌ 非対応| S
        P5[カスタム remark プラグイン] -->|⚠️ 移植が必要| S
        P6[カスタム rehype プラグイン] -->|⚠️ 移植が必要| S
    end

    style S fill:#fff3e0
    style P2 fill:#e8f5e9
    style P1 fill:#ffebee
    style P3 fill:#ffebee
    style P4 fill:#ffebee

Astro 6.4 のロードマップは、Sätteri が将来のメジャーバージョンでデフォルトプロセッサになると明言している。つまり、今あなたには 2 つの選択肢がある:

  1. 今すぐ評価して移植する——プラグイン依存が少なければ、直接切り替え可能
  2. unified に留まってエコシステムの成熟を待つ——ただし 8.0 までには移行を完了する必要がある

判定式:

[ \text{純利益} = \alpha \cdot \text{速度向上} - \beta \cdot \text{プラグイン移行コスト} ]

ドキュメントサイト(プラグインが少なく、コンテンツが多い):(\alpha \gg \beta)、今すぐ切り替える価値あり。 プラグイン多用ブログ(toc + slug + autolink + math + diagram):(\beta) が (\alpha) を上回る可能性あり。

2.4 Sätteri がネイティブでサポートする機能

機能UnifiedSätteri備考
GFM(テーブル、タスクリストなど)✅ プラグイン✅ ネイティブ追加コストなし
Smartypants(スマート引用符)✅ プラグイン✅ ネイティブ追加コストなし
directive 構文⚠️ remark-directive が必要✅ ネイティブ features: { directive: true }よりシンプル
MDAST/HAST プラグイン✅ すべて対応核心的な制約
カスタムコンポーネント✅ MDX✅ MDXSätteri は MDX 対応
数式⚠️ remark-math が必要❌ unified へのフォールバックが必要ハイブリッド運用可能

三、Cloudflare デプロイ:6 つのバインディングを 1 つに圧縮

3.1 過去の手作業

6.4 より前、Astro を Cloudflare にデプロイするには手動処理が必要だった:

// ❌ 6.3 以前 —— 各バインディングを手動で注入
export async function onRequest(context) {
const { request, env, ctx } = context;
const sessionKV = env.SESSION_KV;
const assets = env.ASSETS;
const clientIP = request.headers.get('cf-connecting-ip');
const waitUntil = ctx.waitUntil.bind(ctx);
// その後ようやく Astro のリクエスト処理に到達
return await handleRequest(request, {
sessionKV, assets, clientIP, waitUntil
});
}

注入すべきバインディングとコンテキストは 6 つある:SESSION KV、ASSETS、cf-connecting-ip、waitUntil、locals.cfContext、エラーページルーティング。1 つ欠けると本番で謎の 500 エラーが発生する可能性がある。

3.2 cf() による抽象化

cf(state, env, ctx) はこの 6 つを 1 回の呼び出しに圧縮する:

sequenceDiagram
    autonumber
    participant C as クライアント
    participant F as Fetch Handler
    participant CF as cf(state, env, ctx)
    participant KV as SESSION KV
    participant AS as ASSETS
    participant IP as cf-connecting-ip
    participant WU as waitUntil
    participant A as Astro レンダリング

    C->>F: HTTP リクエスト
    F->>CF: cf() ヘルパー関数を呼び出し
    CF->>KV: KV バインディングを注入
    CF->>AS: 静的アセットを解決
    CF->>IP: 実際のクライアント IP を抽出
    CF->>WU: バックグラウンドタスクを登録
    alt 静的アセットにヒット
        CF-->>F: アセットを返却
        F-->>C: 200 OK + アセット
    else レンダリングが必要
        CF->>A: Astro に転送
        A-->>F: HTML レスポンス
        F-->>C: 200 OK + HTML
    end

実際の設定コードはこうなる:

// ✅ Astro 6.4+
import { defineConfig } from 'astro/config';
import cloudflare from '@astrojs/cloudflare';
export default defineConfig({
output: 'server',
adapter: cloudflare({
advancedRouting: {
cf: true, // 1 行で cf() ヘルパーを有効化
},
}),
});

advancedRouting.cf: true はすべてのバインディングを自動で注入する。手動でコンテキストを組み立てる必要はない。

3.3 Hono ミドルウェア統合

Hono を使用しているチーム向けに、cf() は Hono ミドルウェアとしても公開されている:

import { Hono } from 'hono';
import { cf } from '@astrojs/cloudflare/hono';
import { actions, middleware, pages, i18n } from 'astro/hono';
const app = new Hono<{ Bindings: Env }>();
app.use(cf()); // ← 1 行ですべての Cloudflare バインディングを注入
app.use(actions());
app.use(middleware());
app.use(pages());
app.use(i18n());
export default app;

抽象化後のインターフェース複雑度:

[ \text{統合複雑度}{以前} = \sum{i=1}^{6} \text{バインディング}_i \times \text{ボイラープレート}i ] [ \text{統合複雑度}{以後} = 1 \times \text{cf()} ]

つまり、バインディング数が多ければ多いほど、cf() による簡略化の効果は顕著になる。KV バインディングを 1 つだけ使っているプロジェクトでは効果は限定的だが、KV + D1 + R2 + Queue + AI Gateway をすべて使っているなら、この抽象化の価値は計り知れない。

3.4 開発-本番の一貫性

地味だが重要な改善点:6.4 ではローカルの wrangler 開発サーバーと Cloudflare Edge ランタイムの動作がより近くなった。以前よくあったバグのパターン——ローカルでは動くが本番で落ちる——の多くはバインディング解決の差異に起因していた:

flowchart TB
    subgraph "6.4 以前"
        D1[ローカル開発] -->|動作に差異あり| P1[Cloudflare Edge]
        D1 -->|バグは本番でしか見つからない| D1
        style D1 fill:#ffebee
        style P1 fill:#ffebee
    end

    subgraph "Astro 6.4+"
        D2[ローカル開発<br/>wrangler + cf()] -->|忠実度が高い| P2[Cloudflare Edge]
        style D2 fill:#e8f5e9
        style P2 fill:#e8f5e9
    end

具体的には、以下の差異が 6.4 で大幅に縮小された:

  • KV 名前空間の解決パスが本番と一致
  • 静的アセット ASSETS バインディングの動作が同期
  • cf-connecting-ip がローカルでもモック値を取得
  • エラーページルーティングが手動設定不要に

四、安全なアップグレードパス

4.1 3 段階の移行

Astro 6.4 へのアップグレード戦略は 3 つのフェーズに分解できる:

flowchart LR
    A[フェーズ一:CLI アップグレード] -->|npx @astrojs/upgrade| B[フェーズ二:設定更新]
    B -->|wrangler.jsonc<br/>単一エントリポイント| C[フェーズ三:監査とテスト]
    C -->|Markdown レンダリングを確認<br/>プラグイン互換性を検証| D[本番投入]

    style A fill:#e3f2fd
    style B fill:#fff3e0
    style C fill:#e8f5e9
    style D fill:#f3e5f5

フェーズ一:アップグレード

Terminal window
npx @astrojs/upgrade
# または
bunx @astrojs/upgrade

これによりバージョン番号の更新と依存関係の再インストールが自動で処理される。@astrojs/cloudflare を使用している場合は、アダプターもアップグレードされる。

フェーズ二:設定移行

Terminal window
# 新しい設定ファイル形式を検証
npx astro sync

プロジェクトで src/env.d.ts を使用している場合、Astro 6.4 では新しい型宣言への移行が推奨される:

/// <reference types="astro/client" />
/// <reference types="@astrojs/cloudflare" />

wrangler.jsonc の設定:

{
"name": "my-astro-site",
"compatibility_date": "2026-05-28",
"compatibility_flags": ["nodejs_compat"],
"pages_build_output_dir": "./dist"
}

フェーズ三:監査チェックリスト

チェック項目Unified パスSätteri パス
ビルド時間ベースライン~50% 高速
remarkPlugins✅ 正常動作❌ 移植が必要
rehypePlugins✅ 正常動作❌ 移植が必要
gfm✅ プラグイン対応✅ ネイティブ対応
smartypants✅ プラグイン対応✅ ネイティブ対応
directive 構文❌ プラグインが必要✅ ネイティブ (features: { directive: true })

4.2 移行判断マトリックス

quadrantChart
    title "Sätteri 移行戦略マトリックス"
    x-axis 低プラグイン依存 --> 高プラグイン依存
    y-axis 低ビルド時間感度 --> 高ビルド時間感度
    quadrant-1 "今すぐ移行"
    quadrant-2 "評価と移植"
    quadrant-3 "Unified に留まる"
    quadrant-4 "まずベンチマーク"
    "ドキュメントサイト": [0.2, 0.9]
    "マーケティングブログ": [0.4, 0.6]
    "プラグイン多用ブログ": [0.8, 0.3]
    "EC コンテンツページ": [0.6, 0.7]
    "技術チュートリアルサイト": [0.3, 0.85]
    "企業公式サイト": [0.5, 0.4]

このマトリックスの左上に位置するプロジェクト(ドキュメントサイト、技術チュートリアルサイト)——プラグインが少なくビルド時間が長い——は、今すぐ移行するメリットが最大となる。右下に位置するもの(プラグイン多用ブログ、高度にカスタマイズされたマーケティングサイト)は、まずベンチマークを実施し、プラグインエコシステムが成熟してから切り替えるとよい。

4.3 ハイブリッド運用のエスケープハッチ

Sätteri の速度が必要だが、特定の remark プラグインが欠かせないプロジェクトには、ディレクトリ単位で設定を分ける回避策がある:

import { defineConfig } from 'astro/config';
import { unified } from '@astrojs/markdown-remark';
import { sätteri } from '@astrojs/markdown-sätteri';
export default defineConfig({
markdown: {
processor: unified(),
// 特定のコンテンツコレクションには Sätteri を使用
contentCollections: {
docs: { processor: sätteri() },
blog: { processor: unified() }, // プラグイン対応を維持
},
},
});

この機能はまだ実験段階だが(experimental.contentCollectionProcessorRouting: true が必要)、現実的な中間経路を示している:プラグインを多用するコンテンツには unified を、高速性が求められるコンテンツには Sätteri を使う。


五、実測:実在サイトの移行

ある中規模ドキュメントサイトで実際に移行実験を行った。データは以下の通り:

5.1 サイト特性

指標数値
Markdown ファイル数847
画像数203
カスタム remark プラグイン2(コードハイライト強化 + カスタム callout)
カスタム rehype プラグイン1(カスタム見出しアンカー)
Cloudflare バインディングKV + R2 + D1

5.2 移行手順

  1. CLI アップグレードnpx @astrojs/upgrade、エラーなし
  2. 設定移行remarkPlugins / rehypePluginsprocessor: unified({...}) に移動
  3. Sätteri 評価npx astro check --processor sätteri を実行、2 つのカスタムプラグインに非互換を確認
  4. カスタムプラグインの移植
    • コードハイライトプラグイン → Sätteri ネイティブ対応(features: { syntaxHighlight: true }
    • カスタム callout → Sätteri の transforms API で書き換え(35 行の Rust → JS バインディング)
    • カスタムアンカー → 廃止し、手動 ID に変更
  5. cf() を有効化cloudflare アダプター設定に advancedRouting: { cf: true } を追加、手動バインディングコードを削除

5.3 結果

指標移行前移行後変化
ビルド時間87s42s-52%
CI コスト(月額)~$45~$22-51%
Cloudflare アダプターコード47 行3 行-94%
開発-本番バグ率月に約 2〜3 件0(評価日現在)-100%

六、結論:速度 vs エコシステム

Astro 6.4 は、すべての SSG フレームワークが遅かれ早かれ直面する問いを投げかけている:ネイティブ速度はプラグイン互換性を犠牲にする価値があるのか?

Astro の回答は極めて実践的だ——急ぐ必要はないが、方向性は決まっている。Sätteri は opt-in であり、unified は非推奨になったがまだ削除されていない。この移行ウィンドウはエコシステムに調整の時間を与えている。

今日から持ち帰れる 3 つのポイント:

  1. Markdown パイプラインがプラグイン可能になった——つまり将来的には Python プロセッサ、Go プロセッサ、ブラウザネイティブプロセッサが登場する可能性がある
  2. コンテンツ集約型プロジェクトは今すぐ Sätteri に切り替えて、ビルド時間を半分にできる
  3. Cloudflare ユーザーは cf() を使わない理由がほぼない——6 行のバインディングを 1 行に圧縮し、副作用もない

もう 1 つ、ひそかなシグナルを忘れてはいけない:Sätteri という名前はスウェーデン語で「整理する/整頓する」という意味だ。Astro チームは派手なパフォーマンス・マーケティング用語ではなく、職人的な言葉を選んだ。それは偶然ではない。


参考

このページをシェア