PR
PR

【Astro入門】Astro 5からAstro 6へのアップグレード手順 (Podman環境)

記事内に広告が含まれています。

前回は本番環境の構築を行いました。

前回の記事: https://vpslife.server-memo.net/astro-podman-nginx-deploy/

今回はAstro 6がリリースされたので、現在の環境をAstro 5からAstro 6へバージョンアップしていきます。

Astro 5から6へのアップグレード方法

AstroをPodmanでコンテナ構築している場合、Astro公式が用意している「自動アップグレードツール(CLI)」を使用するのが、一番確実で安全な方法です。

大まかな手順は以下の通りです。

  • Node.jsのアップグレード(22系未満の場合)
  • アップグレードコマンドの実行
  • 自作コードの修正(破壊的変更への対応)
  • 動作確認とビルド

Node.jsのバージョンをアップグレード

Astro 6を動作させるには、「Node.js 22.12.0以上」が必須になります。

コンテナの設定ファイル(compose.yaml)で、ベースのイメージがNode.js 22系以上のものになっていることを確認します。現在使用しているNode.jsのバージョンが22系でも動作するのですが、せっかくの機会なので最新の長期サポートバージョンである「24系」に変更していきます。

compose.yamlの修正とコンテナの再作成

「compose.yaml」の修正部分は以下の通り、「image:」の部分を「node:24」に変更します。

services:
  astro-dev:
    image: node:24   # 変更部分
    container_name: astro-dev
##### 省略 #####

書き換えが終わったら、古いコンテナを破棄して作り直します。
(※ astro-dev はご自身の環境のコンテナ名に読み替えてください)

# 古いコンテナを破棄
$ podman-compose down astro-dev

# 新しいNode.jsでコンテナを起動
$ podman-compose up -d astro-dev

コンテナ起動後、以下のコマンドでNode.jsのバージョンを確認します。

$ podman-compose exec astro-dev node -v
podman-compose version: 1.0.6
['podman', '--version', '']
using podman version: 4.9.3
podman exec --interactive --tty astro-dev node -v
v24.14.1
exit code: 0

v24.x.xと表示されていれば、無事バージョンアップ完了です。

アップグレードコマンドの実行

Astroには、本体と関連パッケージの依存関係が壊れないように一括で引き上げてくれる専用コマンドが用意されています。

コンテナを起動した状態で、以下のコマンドを実行します。

$ podman-compose exec astro-dev npx @astrojs/upgrade

このコマンドを実行すると、ターミナル上で「v6 にアップグレードしますか?(Ok to proceed? (y)) y」「破壊的変更がありますが続けますか?(One package has breaking changes. Continue?)」といった質問がいくつか聞かれますので、画面の指示に従って y (Yes) や Enter を押して進めてください。

これで package.json の中身が自動で最新になります。

コマンド実行例

以下はアップグレードコマンドを実行した際のログになります。

$ podman-compose exec astro-dev npx @astrojs/upgrade
podman-compose version: 1.0.6
['podman', '--version', '']
using podman version: 4.9.3
podman exec --interactive --tty astro-dev npx @astrojs/upgrade
Need to install the following packages:
@astrojs/upgrade@0.7.1
Ok to proceed? (y) y


 astro   Integration upgrade in progress.

      ▲  astro will be updated  from v5.17.1 to v6.1.2 

  wait   One package has breaking changes. Continue?
         Yes

 check   Be sure to follow the CHANGELOG.
         astro https://docs.astro.build/en/guides/upgrade-to/v6/

 ██████  Installing dependencies with npm...

╭─────╮  Houston:
│ ◠ ◡ ◠  Take it easy, astronaut!
╰─────╯
exit code: 0

自作コードの修正

Astroのパッケージアップデートが終わったら、自動アップグレードツールでは変更されない「自作コードの修正」を行います。

Astro 6では従来の「コンテンツコレクションAPI」が完全に廃止され、新しい「Content Layer API(Loader)」へ移行されているため、主にデータの読み込み部分を書き換えます。

私の環境では、以下が修正の対象となりました。

  • src/content/config.ts
  • index.astro
    • src/pages/index.astro
    • src/pages/blog/index.astro
    • src/pages/game/index.astro
  • [slug].astro
    • src/pages/blog/[slug].astro
    • src/pages/game/[slug].astro

src/content/config.tsの修正

修正内容は以下のとおりです。

格納場所とファイル名の変更

config.tsファイルは格納場所と名前が以下のように変更になりました。

  • 変更前: src/content/config.ts
  • 変更後: src/content.config.ts (srcの直下に移動)

Zod(z)ライブラリのインポート方法が変更

Zod(z)ライブラリはJavaScriptやTypeScriptでよく使用される「データ検証(バリデーション)ライブラリ」です。

Astro 5では「import { defineCollection, z } from 'astro:content';」のように「astro:content」から「z」ライブラリをインポートしていましたが、Astro 6からは「import { z } from 'astro/zod';」といったように、別途ライブラリをインポートする記述に変更する必要があります。

データの取得方法が「type: 'content'」から「loader」に変更

Astro 5までの「type: 'content'」は、「src/content/ フォルダの中にあるローカルのマークダウンファイル」を読み込むための機能でした。

Astro 6から採用された「loader」を使用すると、ローカル以外の場所からデータを取得することができるようになります。

「loader」でデータを取得する場合、「glob」関数を使って「どこにあるファイルを読み込むか」を明示的に指示する記述方式になりました。

type: 'content' は使用できなくなりました

「loader」が役割を完全に引き継いだため、古い「type: 'content'」という記述を残したままだとビルドエラーになります。


//import { defineCollection, z } from 'astro:content';
import { defineCollection } from 'astro:content';  // zを削除
import { glob } from 'astro/loaders'; // 新規追加
import { z } from 'astro/zod'; // 新規追加

const blogCollection = defineCollection({

  // loaderを使用するためtype: 'content' を削除
  //  type: 'content',
  // loaderを使って読み込むディレクトリを指定
  loader: glob({ pattern: '**/[^_]*.md', base: "./src/content/blog" }),
  schema: z.object({
    title: z.string(),
    pubDate: z.date(),
  })
});

// type: 'content' の削除とloaderを使って読み込むディレクトリを指定
const gameCollection = defineCollection({
  loader: glob({ pattern: '**/*.{md,mdx}', base: './src/content/game' }),
  schema: z.object({
    title: z.string(),
    pubDate: z.date(),
    author: z.string(),
    description: z.string(),
    tags: z.array(z.string()),
  }),
});

export const collections = {
  'blog': blogCollection,
  'game': gameCollection,
};

index.astroの編集

記事のURL生成などに使われていた「slug」プロパティが廃止され、すべて「id」に統一されました。

新着記事を表示するために使用している、「post.slug.split('/').pop();」の「slug」部分を「id」に変更し「post.id.split('/').pop();」と修正します。

---
import BaseLayout from '../layouts/BaseLayout.astro';
import { getCollection } from 'astro:content';

// blogディレクトリの中身を全て取得する
const allPosts = await getCollection('blog');

// 取得した記事を「日付の新しい順(降順)」に並び替える
allPosts.sort((a, b) =>
  b.data.pubDate.valueOf() - a.data.pubDate.valueOf()
);
// 新着記事を最新から10件だけを切り取って、recentPostsに入れる
const recentPosts = allPosts.slice(0, 10);
---

<BaseLayout>
  <main>
    <h2>こんにちは!</h2>
    <p>何も渡さなくても、自動で「Game Life」とデフォルト説明文が適用されます!</p>

    <section style="margin-top: 40px;">
      <h3>新着記事</h3>
      <ul>
        {
          recentPosts.map((post) => {
            // post.slug.splitをpost.id.splitに変更
            // const cleanSlug = post.slug.split('/').pop();
            const cleanSlug = post.id.split('/').pop();
            
            return (
              <li style="margin-bottom: 10px;">
                <a href={`/blog/${cleanSlug}/`}>{post.data.title}</a>
                <span style="color: #666; font-size: 0.9em; margin-left: 10px;">
                  {post.data.pubDate.toLocaleDateString('ja-JP')}
                </span>
              </li>
            );
          })
        }
      </ul>
    </section>
  </main>
</BaseLayout>

[slug].astroの修正

Astro 6からはデータと変換ツールが切り離されたため、専用の「render」関数をインポートして使います。

「entry.render()」が廃止されたので、「render(entry)」を使ってコンテンツを展開します。

「slug」プロパティが「id」に変更されているので、「index.astro」での修正と同じように、「entry.slug.split('/').pop();」の「slug」部分を「id」に変更し「entry.id.split('/').pop();」と修正します。

 
---
// getCollection に加えて、新しく「render」関数をインポートします
// import { getCollection } from 'astro:content';
import { getCollection, render } from 'astro:content';
import BaseLayout from '../../layouts/BaseLayout.astro';

export async function getStaticPaths() {
  const blogEntries = await getCollection('blog');
  
  return blogEntries.map(entry => {
    // entry.slug ではなく entry.id を使用します
    // const cleanSlug = entry.slug.split('/').pop();
    const cleanSlug = entry.id.split('/').pop();

    return {
      params: { slug: cleanSlug },
      props: { entry },
    };
  });
}

const { entry } = Astro.props;

// Astro 6では entry.render() が廃止されました。
// 代わりにrender(entry) を使ってコンテンツを展開します
// const { Content } = await entry.render();
const { Content } = await render(entry);
---

<BaseLayout pageTitle={entry.data.title} pageDescription={entry.data.description}>
  <article>
    <h1>{entry.data.title}</h1>
    <p>公開日: {entry.data.pubDate.toLocaleDateString('ja-JP')}</p>
    <p>著者: {entry.data.author}</p>
    <hr />
    <Content />
  </article>
</BaseLayout>

never等の型エラーが出た時の対処法

コードの修正が終わっても、VSCode上でnever型になるなどのエラーが消えない場合があります。

これはAstroの新しい型定義がエディタに認識されていないことが原因です。

この場合、以下の手順で試してみてください。

コンテナ内で型定義を同期する

「content.config.ts」の内容をもとに、コンテナ内でAstroの型を明示的に生成するコマンドを実行します。

$ podman-compose exec astro-dev npx astro sync

TSサーバの再起動

content.config.tsを開いた状態で、「Ctrl + Shift + P」でコマンドパレットを開きます。(Macは Cmd + Shift + P)

コマンドパレットにRestart TS server と入力して実行するとTSサーバが再起動されます。

これでもだめなら、コンテナの再起動とVSCodeの再起動を行ってみてください。

コンテナの再起動は以下のコマンドで実行します。

$ podman-compose restart astro-dev

ビルドと動作確認

すべての修正が終わったら、開発用コンテナを起動して動作確認とビルド作業を行います。

まずは、ブラウザで開発環境にアクセスして動作確認を行います。

表示に問題がないことを確認したあとに、ビルド作業を行いHTMLファイルが問題なく「dist」ディレクトリに生成されるかテストします。

$ podman-compose exec astro-dev npm run build

ビルドが正常に完了したら、ブラウザでサイトが正常に表示されることを確認してください。

以上で、Astro 6への移行作業は完了です!

コメント

タイトルとURLをコピーしました