はじめに

このブログはAstro 5で構築しており、記事にはタグを付けて分類しています。

タグ別の記事一覧ページはsrc/pages/tags/[tag].astroという動的ルーティングで実装していましたが、 「CI/CD」のようにスラッシュを含むタグを追加したところ、ビルドが失敗しました。

この記事では、問題の原因と、キャッチオールルートを使った解決方法を記録します。

発生した問題

ChocolateyパッケージのCI/CD移行記事を追加した際、タグに「CI/CD」を指定しました。

---
title: "ChocolateyパッケージのCI/CDをAppVeyorからGitHub Actionsに移行した記録"
tags: ["GitHub Actions", "Chocolatey", "PowerShell", "CI/CD", "Program"]
---

この状態でpnpm buildを実行すると、次のエラーが発生しました。

/tags/CI/CD/index.html  Missing parameter: tag
  Stack trace:
    at getParameter (file:///...astro/dist/core/routing/manifest/generator.js:17:13)

原因

原因は、Astroの動的ルーティング[tag].astroがスラッシュを含むパラメーターに対応していないことです。

getStaticPaths()でタグ「CI/CD」を返すと、Astroは/tags/CI/CD/index.htmlを生成しようとします。

しかし、[tag]は単一のパスセグメントにしかマッチしないため、CI/CDのスラッシュがパス区切りとして解釈されてしまいます。

結果として、/tags/CI/CD/部分がtagパラメーターとして認識できず、Missing parameter: tagエラーになります。

# Astroの解釈
/tags/[tag]  →  /tags/CI/CD  →  NG([tag] = "CI" で "CD" が余る)

# 期待する動作
/tags/[...tag]  →  /tags/CI/CD  →  OK([...tag] = "CI/CD")

解決方法

[tag].astroをキャッチオールルート[...tag].astroにリネームしました。

キャッチオールルート(rest parameters)は、 スラッシュを含むパスセグメント全体を1つのパラメーターとしてキャプチャします。

変更内容

変更はファイル名のリネームのみです。

ロジックの変更は不要でした。

# Before
src/pages/tags/[tag].astro

# After
src/pages/tags/[...tag].astro

getStaticPaths()の実装はそのままです。

export async function getStaticPaths() {
  const allPosts = await getCollection("posts");
  const allTags = new Set<string>();

  allPosts
    .filter((post) => !post.data.draft && post.data.tags)
    .forEach((post) => {
      post.data.tags?.forEach((tag: string) => allTags.add(tag));
    });

  return Array.from(allTags).map((tag) => ({
    params: { tag },
    props: { tag },
  }));
}

タグ「CI/CD」の場合、params: { tag: "CI/CD" }がそのまま/tags/CI/CD/index.htmlを生成します。

タグリンク

テンプレート内のタグリンクも変更不要です。

<!-- タグ一覧ページ -->
<a href={`/tags/${tag}`}>

<!-- 記事詳細ページ -->
<a href={`/tags/${tag}`} class="badge bg-primary">

tagが「CI/CD」の場合、/tags/CI/CDというリンクが生成され、キャッチオールルートに正しくマッチします。

動的ルーティングの使い分け

Astroの動的ルーティングには2種類あります。

ルーティングファイル名マッチ対象用途
動的ルート[param].astro単一パスセグメントスラッシュを含まないパラメーター
キャッチオールルート[...param].astro複数パスセグメントスラッシュを含む可能性のあるパラメーター

今回のケースでは、タグ名にスラッシュが含まれる可能性があるため、キャッチオールルートが適切です。

なお、キャッチオールルートは単一セグメントのパラメーターにもマッチするため、 既存のスラッシュを含まないタグ(「Program」「Astro」など)もそのまま動作します。

影響範囲の確認

今回の変更で影響を受けるのは、タグ別記事一覧ページのルーティングのみです。

次の箇所は変更不要でした。

  • src/pages/tags/index.astro(タグ一覧): リンク生成のみなので影響なし
  • src/pages/posts/[...slug].astro(記事詳細): タグへのリンクは/tags/${tag}で変更不要
  • src/pages/posts/index.astro(記事一覧): 同上
  • src/pages/index.astro(トップページ): 同上

おわりに

Astroの動的ルーティングで、スラッシュを含むタグ名に対応しました。

変更はファイル名を[tag].astroから[...tag].astroにリネームするだけで、 ロジックやテンプレートの修正は不要でした。

タグ名や、スラッシュを含む可能性のあるパラメーターを扱う場合は、 最初からキャッチオールルートを使っておくと、このような問題を未然に防げます。

参考サイト