ryota2357

Astro.jsをv4からv5にアップグレードした

投稿日:

更新日:

この記事は ryota2357 Advent Calendar 2024 の 8 日目の記事です。

5 日ほど前の 12/3 に Astro.js v5 がリリースされた。 この v5 の一番の注目点は Content Layer であろう。

このサイト現在は Astro.js で構築されている。 今回 v5 がリリースされたので Content Collection で管理・生成されていた各記事を Content Layger に移行した。 また、ViewTransitions コンポーネントの名前変更と、TypeScript 型ファイルの移動の対応も行った。 これら v4 から v5 への移行で行ったことを書いていく。

コミット (PR)

変更は ryota2357/ryota2357.com#309 にて行われた。 Renovate のバージョンアップ PR にコミットを繋げている。

PRのコミット

Content Layger

Experimental の時に Content Layer を使用した日本語記事があったのでこれを参考に導入した。コミットは f6362ca

Astro4.14 Content Layer API - 趣味のNote

記事(markdown)の移動

Content Collection の時は Content を置けるのが src/content 以下に制限されていたが、Content Layger ではそれがなくなった。 これまでブログ記事は src/content/blog においていたのを content/blog に移動した。

なお、Content Layger の設定は Content Collection の時と同様、src/content/config.ts で行うので、これは動かしてはいけない。

また、僕は Content Collection で記事以外に src/Content/works/ 以下で yaml ファイルを管理している。 今回これらの Content Layger への移行は行わなかった。 このことから、Astro.js v5 では Content Collection と Content Layger の共存可能であることがわかった。

src/content/config.ts の修正

記事を Content Layger に移行するため該当部分を修正する。 Content Layger に移行せず、Content Collection を引き続き使用するものに関しては修正する必要はない。

 import { defineCollection, z } from "astro:content";
+import { glob } from "astro/loaders";

 const blogCollection = defineCollection({
-  type: "content",
+  loader: glob({ pattern: "**/*.md", base: "./content/blog" }),
   schema: z.object({
     title: z.string(),
     postdate: z.coerce.date(),
     update: z.coerce.date(),
     tags: z.array(z.string()),
     draft: z.boolean().optional().default(false),
   }),
 });

CollectionEntry<C>["slug"]CollectionEntry<C>["id"] にする

Content Layger にすると、getCollection で取得できていたオブジェクトから slug フィールドがなくなる。 おそらく型エラーがでているはず (もしくは astro check とかででるはず) なので、それらを id フィールドに書き換える。

src/pages/blog/[year]/[slug].astro
        <ul class="mx-0 my-4 grid grid-flow-col justify-between gap-3">
          <li>
            {prevPost && (
-             <a href={path.join("/blog", prevPost.slug, "/")} rel="prev">
+             <a href={path.join("/blog", prevPost.id, "/")} rel="prev">
                ← {prevPost.data.title}
              </a>
            )}
          </li>
          <li>
            {nextPost && (
-             <a href={path.join("/blog", nextPost.slug, "/")} rel="next">
+             <a href={path.join("/blog", nextPost.id, "/")} rel="next">
                {nextPost.data.title} →
              </a>
            )}

先ほどあげた参考記事では [...slug].astro のファイル名を [...id].astro に変えていたが、僕は変えず、[...slug].astro のままにした。 フィールド名は確かに id に変わったが、slug の方が具体的なので、コード・ファイルの意味がわかりやすいと思ったからだ。

render の変更

getCollection で取得できたオブジェクトから render フィールドがなくなった。 astro:content から render 関数を import してそれを利用するようだ。

src/pages/blog/[year]/[slug].astro
 import path from "node:path";
 import { Image } from "astro:assets";
+import { render } from "astro:content";
 import { util, allBlogCollection } from "@/content";

 ...省略

 type Props = InferGetStaticPropsType<typeof getStaticPaths>;
 const { post, prevPost, nextPost } = Astro.props;
-const { Content } = await post.render();
+const { Content } = await render(post);
 const formatTime = (time: Date) => dayjs(time).format("YYYY/MM/DD (HH:mm)");
 ---

ViewTransitions コンポーネントの名前変更

v5 での Breaking Changes のうちの 1 つである。 コミットは 4b9d2bf

Upgrade ガイド (Renamed: <ViewTransitions /> component) に書いてある通りだが、ただ名前を変えればいいだけである。

 import "destyle.css";
-import { ViewTransitions } from "astro:transitions";
+import { ClientRouter } from "astro:transitions";
 import Footer from "@/components/Footer.astro";
 import HeadSEO from "@/components/HeadSEO.astro";
         }
       }
     </style>
-    <ViewTransitions />
+    <ClientRouter />
   </head>
   <body>

TypeScript 型ファイルの移動

src/env.d.ts から .astro/types.d.ts に移動された。コミットは 0f6a078

これも Upgrade ガイド (Changed: TypeScript configuration) の通りに修正すれば良い。

最後に

Astro.js は v3 の頃に Gatsby.js から移行したので、今回でメジャーバージョンアップは 2 回目だった。 v3 -> v4 は getGET に変えるくらいの小さなものだったと記憶しているが、今回 v4 -> v5 は Content Layger への移行もありコードをいくつか書き換えた。 しかしその作業は参考記事の存在もありスムーズに行えた。