👏

[2023年6月版]Astro.js 小ネタ集 その6 JSONコレクション

2023/06/23に公開

前回に引き続きAstro.jsでの小ネタを紹介していきます。

ここでは Astro 2.5 で追加された JSON コレクションについてご紹介いたします。

JSON コレクション

Astro 2.5 からコレクションでJSON、yaml形式のファイルがサポートされました。
これでタグとカテゴリとアイコンのパス、などのようなメタデータだけの集合をコレクションとして扱えるようになりました。
個人ブログでは"タグ"の定義にJSONコレクションを使っています。
ここでは "タグ" コレクションの使用方法を解説いたします。

1. 準備

まず、"タグ" コレクションの型をconfig.tsに定義します。

src/content/config.ts
...
const tags = defineCollection({
  type: 'data',
  schema: z.object({
    title: z.string(),
    icon: z.string(),
  }),
});
...
export const collections = {
  tags,
  blog,
};

このようにJSONやYAMLのコレクションは type: 'data'を指定します。
ここでは titleicon属性を持つオブジェクトとして定義します。

2. コレクションに追加

続いて、この定義に合わせてcontent以下のようなJSONファイルを作成します。

src/content/tags/redis.json
{
    "title": "Redis",
    "icon": "redis" 
}

個人ブログではURLの一部にも使用されるタグの"キーワード"に対する画面上の表示で使用されるメタ情報をこのような形で定義、管理しています。

3. ページでの利用

そして、実際の astro ファイル上では以下のようにブログ記事のメタ情報と併せてJSONコレクションのエンティティを取得しています。

src/pages/tags/index.astro
import { getCollection, getEntry } from 'astro:content';

...

const tags = await Promise.all([...(await getCollection('blog'))
    .flatMap((blog) => blog.data.tags)
    .reduce((uniq, tag) => uniq.add(tag), new Set<string>())]
    .map(async (tag) => {
        const e = await getEntry('tags', tag);
        return e ?? {
            id : tag,
            collection: 'tags',
            data: {
                title : tag,
                icon : ``,
            }
        };
    }));  

(await getCollection('blog')).flatMap((blog) => blog.data.tags) でブログ記事に含まれるタグのリストを取得しています。

ブログにタグのリストがついているので、ブログのリスト(=タグのリストのリスト)からただのタグのリストに直すために flatMap を使用しています。

続いて .reduce((uniq, tag) => uniq.add(tag), new Set<string>()) でリストをSetにすることでタグの重複を除去します。
さらにSetはこのままでは .mapなどのメソッドが使えないので、[... タグのSet] の形式でリストに展開します。

そして、await getEntry('tags', tag)タグ名.json を読み込みます。ファイルがなければ、{id:tag, collction: 'tag', ...} というコレクションのエンティティをその場で生成してタグのメタ情報リストを作成しています。

後はブログのコレクションと同じように tags[0].data.title など値をHTMLに埋め込めばOKです。

ちなみに、.map(async ...) を使用すると、この戻り値はPromiseの配列(Promise<T>[])となりますので、最初の Promise.all()でPromiseの配列(Promise<T>[])を配列(T[])にしています。ややこしい。。。

今回はここまでといたします。

Discussion