Astro でも gatsby-plugin-mdx の excerpt を再現
2024/11/22
はじめに
Gatsby から Astro に乗り換えたのですが、 Gatsby では簡単に利用できていた記事本文の抜粋機能が Astro ではちょっと工夫しないと利用できなかったので、 対応内容をまとめてみました。
Gatsby で利用していた gatsby-plugin-mdx の excerpt
情報の再現方法についてです。
事前調査
gatsby-plugin-mdx における excerpt
について
コードまでは追っていませんが、ドキュメントを読む限り、 内部的には rehype-infer-description-meta を利用しているとのことでした。
rehype-infer-description-meta
単体では、抽出した description
を file.data.meta.description
に格納するだけの rehype
プラグインなので、
gatsby-plugin-mdx
はおそらく、格納された結果を excerpt
として参照できるようにしているのかと推測します。
Astro における frontmatter
加工について
Astro でも Gatsby と同様、frontmatter
を利用できるわけですが、remark プラグイン、
もしくは rehype プラグインを通して加工することができます。
ドキュメントにサンプルも記載されていますが、
プラグイン内で file.data.astro.frontmatter
に対して変更を加えることで、frontmatter
を加工できるようです。
ただし、加工結果を取得する場合は、必ず render()
メソッドを呼ぶ必要があります(ドキュメント)。
方針
rehype-infer-description-meta
が生成した file.data.meta.description
を file.data.astro.frontmatter.excerpt
に格納するだけのプラグインを自作する。
実装内容
rehype-infer-description-meta-astro-adapter.ts
import type { Root } from "hast";
import type { Plugin } from "unified";
const rehypeInferDescriptionMetaAstroAdapter: Plugin<[], Root> = () => {
return (_, file) => {
(
file.data.astro as {
// biome-ignore lint/suspicious/noExplicitAny:
frontmatter: Record<string, any>;
}
).frontmatter.excerpt = file.data.meta?.description ?? "";
};
};
export default rehypeInferDescriptionMetaAstroAdapter;
astro.config.mjs
// @ts-check
import { defineConfig } from "astro/config";
import rehypeInferDescriptionMeta from "rehype-infer-description-meta";
import rehypeInferDescriptionMetaAstroAdapter from "./src/plugins/rehype-infer-description-meta-astro-adapter";
export default defineConfig({
markdown: {
rehypePlugins: [
[rehypeInferDescriptionMeta, { truncateSize: 140 }],
rehypeInferDescriptionMetaAstroAdapter,
],
},
});
呼び出し例
export interface PostMeta {
slug: CollectionEntry<"posts">["slug"];
data: InferEntrySchema<"posts"> & {
excerpt: string;
};
}
export const getPostMetasAsync = async (options?: {
filter?: (entry: CollectionEntry<"posts">) => boolean;
}): Promise<PostMeta[]> => {
const filter = options?.filter;
const posts = await getCollection("posts", filter);
posts.sort((a, b) => {
return b.data.date.valueOf() - a.data.date.valueOf();
});
return await Promise.all(
posts.map(async (post) => {
const { remarkPluginFrontmatter } = await post.render();
const data: PostMeta["data"] = {
title: remarkPluginFrontmatter.title,
date: remarkPluginFrontmatter.date,
tags: remarkPluginFrontmatter.tags,
excerpt: remarkPluginFrontmatter.excerpt,
};
return {
slug: post.slug,
data,
};
}),
);
};
おわりに
今回は gatsby-plugin-mdx
に倣って rehype-infer-description-meta
を利用したので、データ構造を合わせるためだけの rehype プラグインを自作しましたが、
やりたいこと的には remark プラグインの方があっていそうな気もしますし、色々な解決策がありそうですね。