Astro 2.0 の新機能、Content Collections API を使って安全に MD / MDX コンテンツを管理する
昨日(2023/01/24) Astro が 2.0 になった 🎉
npm create astro@latest
2.0 のポイント
- Content Collections: MD と MDX を型安全にするで
- Hybrid Rendering: SSG にするか SSR にするかをページ毎に選べるようにしたで(Next.js とか SvelteKit にあるやつ)
- エラーメッセージめっちゃ見やすくしたで
- HMR 改善したで
- Vite 4.0 に対応したで
- 新しいロードマップ公開したで
目玉は Content Collections と Hybrid Rendering 。
今回は Content Collections について書く。
Content Collections API
Astro 2.0 は、現代の Web 開発において見落とされがちな問題を取り扱うことを目的としています:Markdown / MDX を扱うことは難しいです。
MD/MDX ってファイル大量になりがちで、管理コスト高い割には型チェックもなんも効かんのまじ DX 終わりすぎ。
ってことで Content Collections API ができた。
公式で説明されている4つのポイント。
- Type Safe な MD / MDX
- わかりやすいエラーメッセージ
- 型の自動生成
- インライン型エラー、オートコンプリート!
ちなみにマークダウンに関するプラグインはビルトインになったのでもう astro add
しなくてもいい。
長い説明はええから使い方見せてや
プロジェクトの src/content/
に設定と コレクション と呼ばれるものを作成する。
src/content/
1. 必要なファイルの作成 似たような MD や MDX ファイルをグループ化するための特別なディレクトリ。ここの中に
src/content/<コレクション名>/<エントリー名>.{md,mdx}
の形でファイルを作ると コレクション として認識される。
設定ファイルは
src/content/config.{js,mjs,ts}
なお src/content
は予約済みのディレクトリなので、この用途以外では使えないし、エントリーには.md
, .mdx
しか使えない。
あとコレクションのネストもできない(サブディレクトリはおっけー)。
blog/
とか docs/
とか newsletter/
みたいな感じで分ける。
2. コレクションスキーマの定義
次に設定ファイルを書く。
ここで各コレクションがどんなデータを持ってるかを定義する。
// 1. `astro:content` からユーティリティをインポート。
// ここで **`astro:content` が見つからん** って怒られる人は本記事の一番下参照
import { defineCollection } from 'astro:content';
import { blogSchema, newsletterSchema } from '../schemas';
// import { blogSchema } from 'my-blog-theme'; // サードパーティ製でも 🙆🏻♂️
// 2. 検証したいコレクションごとにスキーマを定義する。
const blogCollection = defineCollection({ schema: blogSchema });
const newsletterCollection = defineCollection({ schema: newsletterSchema });
// 3. コレクションを登録するために `collections` オブジェクトをエクスポート。
// `key` が 1. で作った `<コレクション名>` に対応している。
export const collections = {
// blog: defineCollection({ /* ... */ }), // 直接書いても 🙆🏻♂️
blog: blogCollection,
newsletter: newsletterCollection,
};
zod 💎 もいける
// `astro:content` から インポート。
import { z, defineCollection } from 'astro:content';
const blogCollection = defineCollection({
schema: z.object({
title: z.string(),
tags: z.array(z.string()),
image: z.string().optional(),
}),
});
slug
プロパティは予約語
コンテンツエントリーは自動で URL フレンドリーな slug
を生成するので、 slug
プロパティを再定義することはできない。
defineCollection({
schema: z.object({
slug: z.string(), // 🙅🏻♂️🙅🏻♂️🙅🏻♂️🙅🏻♂️🙅🏻♂️
}),
});
3. コレクションを照会する
あとは使いたいページで getCollection()
か getEntryBySlug()
を使ってデータを取得。
よしなにする。
---
import { getCollection, getEntryBySlug } from 'astro:content';
// コレクションからすべてのエントリーを取得する。引数は `<コレクション名>`。
const allBlogPosts = await getCollection('blog');
// コレクションから単一のエントリを取得する。引数は `<コレクション名>` とエントリーの `slug`。
// 👇🏻 は `src/content/blog/enterprise.{md,mdx}` を取得した例
const oneBlogPost = await getEntryBySlug('blog', 'enterprise');
---
Type Safe な MD / MDX・わかりやすいエラーメッセージ
ポイント 1 と 2 のやつ。
型チェックが Front Matter に対して効いているし、
- 問題のあるファイルの正確なファイル名
- 理解するのに役立つファイルのスニペットと、そのファイルをコード エディターで直接開くためのリンク
- デバッグに役立つドキュメントのヒント
と以前と比べて詳細なエラーを出してくれるようになった。
型の自動生成
ポイント 3 のやつ。
自動生成された型やデータは .astro/
に入れられる。
型が生成されるタイミングは astro {dev,build,sync}
した時と、 astro dev
中にコレクションをいじった時。
👇🏻 自動生成された型。
.astro/
には型以外にも色々入ってくるっぽいことが書かれているけど、今回は型しか生成されなかった。
slug
が自動で定義されているのも確認できる。
インライン型エラー、オートコンプリート!
ポイント 4 のやつ 🥺
バグ?
なぜか一番最初だけ astro {dev,build}
しないと astro:content
が見つからん って文句言われる。
TS でエラー出てても普通に動くので、一回 astro dev
なり astro build
なりする。
Discussion