2026年6月、Astro 7 がリリースされました。自分のブログ redamoon.net は Astro 5 系で動いていたので、ブランチ feature/astro-7-upgrade を切ってアップグレードに着手し、マージまで届けました。
ここでは移行前に立てた計画、実際に手を入れた箇所、CI でハマった点をまとめます。
方針(確定事項)
今回の制約と方針は次のとおりです。
- 必ず Astro 7 にする。
@astrojs/*やwranglerなど周辺ライブラリも合わせて更新する。 - Markdown は新パイプライン Sätteri に寄せる。ただし記事本文は実質
zenn-markdown-html系で描画されるため、Astro 標準パイプライン切替の影響は小さい。移行後は全記事をビルドして目視確認する。 - 対応範囲は Astro 本体アプリ(サイト + scrap ページ)のみ。
workers/scrap-api(Hono / Cloudflare Worker)はコード変更せず、ビルド・デプロイ・API 疎通の互換確認だけ行う。src/fetch.ts(astro/hono)の新規導入はスコープ外。
「壊さない」を最優先にした結果、新機能の導入や大きな構成変更は今回のブランチから外し、あとで別タスクに回す判断をしました(後述)。
現状把握(移行前に確認したこと)
記事レンダリングは Astro 標準 Markdown と独立
最初に確認したのは「記事本文が Astro の Markdown パイプラインに依存しているか」です。結論は 依存していない でした。
| コンテンツ | レンダリング経路 |
|---|---|
| log 記事 | zenn-markdown-html / markdown-it(src/pages/log/post/[...slug].astro、src/utils/logMarkdownToHtml.ts) |
| slides | Marp(src/pages/slides/[slug].astro) |
| scrap 詳細 | client:only="react"(src/pages/scraps/[slug].astro) |
そのため astro.config.mjs にあった remark-gfm / remark-math / remark-toc / remark-collapse / rehype-katex は、実出力への寄与がほぼありません。KaTeX 数式は Marp(slides)側で完結しています。
Markdown を Sätteri に寄せても記事本文が壊れるリスクは小さい一方、レガシー API の移行が本丸になります。
主リスクはレガシー API
**Astro.glob(...)** —index.astro、search.astro、tags/配下、log/post/配下などで広く使われていた- レガシー Content Collections — loader なしの
defineCollection、entry.slug/entry.body参照(src/content/config.ts、src/utils/log.ts、slides ページ) - scrap 機能 — 一覧/編集は
client:loadの React +SCRAP_API_BASE、詳細は SSR(prerender = false)+client:only。サイト本体の Cloudflare デプロイはwrangler.site.toml経由
作業手順と実施内容
計画どおり、次の順で進めました。
1. ブランチ作成とベースライン
feature/astro-7-upgrade を作成し、移行前に pnpm build を実行して dist の生成物と警告を控えました。
2. Astro 7 へアップグレード
pnpm dlx @astrojs/upgrade で astro と @astrojs/* を一括更新しました。おおむね次の構成です。
| パッケージ | 移行前 | 移行後 |
|---|---|---|
| astro | 5.18 系 | 7.0.0 |
| @astrojs/cloudflare | 旧系 | 14.0.0 |
| vite | 6 系 | 8.0.16 |
| @astrojs/react | 旧系 | 6.0.0 |
react / react-dom は 18 系のまま @astrojs/react の peer を満たすことを確認しました。
@astrojs/tailwind は Astro 7 向けに非対応だったため、Tailwind v3 を維持したまま PostCSS 構成(postcss.config.cjs)へ切り替えました。
3. Markdown パイプラインの Sätteri 化
astro.config.mjs から remarkPlugins / rehypePlugins / shikiConfig を撤去し、Astro 7 のデフォルト(Sätteri)に任せました。
記事本文は zenn-markdown-html 経由のため、標準パイプラインへの依存はほぼありません。もし標準 Markdown 出力に差分が出た場合は、@astrojs/markdown-remark の unified() へ退避する保険も計画に含めていましたが、今回は Sätteri のままビルドが通りました。
4. レガシー API の移行(主作業)
**Astro.glob** →import.meta.glob(..., { eager: true })(src/utils/contentPosts.tsに集約。rss.xml.tsや[ogTitle].svg.tsの既存パターンに合わせた)- Content Collections → Content Layer API(
globloader をsrc/content.config.tsに追加) **entry.slug/entry.body** →entry.idベースの参照へ変更(src/utils/log.ts、slides ページなど)**ViewTransitions**→ClientRouter
5. Rust コンパイラ厳格化への対応
Astro 7 の新コンパイラは HTML 自動補正をしない・未閉じタグでエラー・JSX 空白の畳み込みなど、挙動が厳しくなります。今回のビルドでは大きなテンプレート修正は不要でしたが、表示崩れが出た場合は {' '} で空白を明示する、という方針を計画に入れていました。
6. ビルドと静的出力の検証
pnpm build(astro build && jampack ./dist/client)を通し、次を確認しました。
- log 記事 HTML、CSS/スタイル
public/配下の画像・SVG・favicon.svg、OG 画像- RSS、sitemap
jampack も「No issues」で完了しています。
7. scrap 機能の互換確認
workers/scrap-api(Hono)はコード変更なし。wrangler deploy --dry-run で D1/R2 バインディングが維持されることを確認しました。
scrap 詳細(SSR + client:only)が Worker ランタイムで例外を出さないことも、デプロイ dry-run とビルド成果物の構成で確認しています。
なお zenn-markdown-html の dependencies 版数(0.1.150)と pnpm.patchedDependencies の対象版数(0.1.162)の不整合は、今回のスコープ外として残しています(脚注の docId 生成パッチが正しく当たるかの整備は別タスク)。
8. Cloudflare デプロイ確認
@astrojs/cloudflare v14 では、従来の dist/_worker.js ではなく次の構成に変わります。
- 静的アセット:
dist/client/ - Worker エントリ:
dist/server/entry.mjs - デプロイ用設定:
**dist/server/wrangler.json**(アダプタが生成)
deploy:site は次の流れです。
pnpm build && wrangler deploy --config ./dist/server/wrangler.json
ルートの wrangler.toml は scrap-api(Hono)専用のままです。Astro サイト側は wrangler.site.toml をアダプタの configPath で参照し、KV(SESSION)やゾーンルート(redamoon.net/*)を引き継ぎます。
prerenderEnvironment: "node" も指定しています。workerd 上の prerender はグローバルスコープでの非同期 I/O 制約があり、OG 画像生成(フォント取得)や getStaticPaths 内の fetch が動かなくなるためです。
9. 仕上げ
README.md のバージョン・コマンド記述を更新しました。pnpm lint はリポジトリに ESLint 設定がなく、今回は触れていません。
今回のスコープ外にしたこと
アップグレード本体から外し、あとで別タスクに回す項目です。リポジトリ内では issue として起票していますが、ここでは内容だけ書きます。
- Advanced Routing — Astro 7 の
src/fetch.ts(astro/hono)でリクエストパイプライン(i18n / auth / middleware / pages)を明示的に合成する導入検討 - Route Caching —
Astro.cache/routeRulesによる SSR 応答のキャッシュ方針(scrap 詳細ページなど) - Cloudflare CDN Cache Provider —
cacheCloudflare()によるエッジキャッシュ委譲(Workers Cache は private beta のため待ち) - AI Enhancements —
astro dev --backgroundや JSON logging など、エージェント/CI 向けの運用改善 - Tailwind v4 +
@tailwindcss/viteへの移行 — 今回は v3 + PostCSS で表示崩れリスクを避けた **zenn-markdown-htmlの版数整合** — dependencies と patch 対象版数の不一致解消、ブラウザでの脚注描画確認
いずれも「まず Astro 7 で壊さず動かす」ことを優先して、今回のブランチには入れませんでした。
Cloudflare Pages CI でハマった Node.js バージョン
マージ後、Cloudflare Pages のビルドで2段階ハマりました。計画には含まれていませんでしたが、実運用で必須の修正です。
1. Astro 7 の最低要件(>= 22.12.0)
Node.js v20.10.0 is not supported by Astro!
Please upgrade Node.js to a supported version: ">=22.12.0"
.nvmrc が 20.10.0 のままだったのが原因です。22.12.0 に上げて解消しました。
2. Vite 8 の registerHooks(>= 22.15.0、実運用は 22.16.0 推奨)
The requested module 'node:module' does not provide an export named 'registerHooks'
Astro 7 の起動チェックは >=22.12.0 ですが、Vite 8 が使う node:module.registerHooks は Node 22.15.0 以降で提供されます。22.12.0 では Astro のバージョンチェックは通っても、設定読み込みで落ちます。
最終的に .nvmrc / .node-version を 22.16.0(Cloudflare Pages v3 ビルドイメージのデフォルト)に揃え、package.json の engines.node は >=22.15.0 にしました。
CI で Node を上げるときは「フレームワークの最低要件」と「依存ツールが使う Node API」の両方を見る必要がある、という教訓です。
リスクと留意点(振り返り)
計画時に挙げていた不確実性は、おおむね次のとおりでした。
- レガシー Content Collections /
Astro.glob— ビルドで早期にエラーが出たため、手順4の移行が必須だった **@astrojs/tailwind**— Astro 7 非対応のため PostCSS 構成へ分岐した- Sätteri 化 — 実記事は zenn/markdown-it 経由のため影響は限定的。unified 退避は不要だった
- Node.js バージョン — 計画外だったが、Astro の要件と Vite 8 の隠れ要件の差が CI で顕在化した
まとめ
Astro 7 への移行は「パッケージを上げる」だけでは終わりません。レガシー API の置き換え、Cloudflare アダプタの出力構成変更、Node.js のバージョン要件までセットで見る必要がありました。
記事本文は zenn-markdown-html 経由だったので Markdown パイプライン切替のリスクは小さかった一方、壊れやすいのはビルド・デプロイ・CI の周辺でした。次は Advanced Routing や Tailwind v4 化など、スコープ外にした項目を別ブランチで進めたいと思います。