主にプログラミング関連のメモ帳 ♪(✿╹ヮ╹)ノ
書いてあるコードは自己責任でご自由にどうぞ。記事本文の無断転載は禁止です。
2022/02/20
ここ最近は Unity の記事ばかりでしたが、今回は Node.js のお話と言うことで、最近お気に入りのライブラリー、 contentlayer について紹介します。
contentlayer は名前の通り、 Next.js などのコンテンツ周りを良い感じに取得してくれたり、コンパイルしてくれるライブラリです。
これを使うことで、簡単に Markdown などのドキュメントから静的サイトを構築することが出来ます。
ということで、まずは導入方法から。
いつも通り、 yarn 経由で導入します。
$ yarn add contentlayer next-contentlayer
導入したら、 contentlayer.config.ts
という設定ファイルを作成します。
このブログの場合は、以下のような設定になります。
import {
ComputedFields,
defineDocumentType,
defineNestedType,
makeSource,
} from "contentlayer/source-files";
import { remark } from "remark";
import strip from "strip-markdown";
const getSummarizedText = (markdown: string) => {
const text = remark().use(strip).processSync(markdown).toString();
const firstSentence = text.indexOf("。");
if (firstSentence <= 120) {
return text.substring(0, firstSentence + 1);
}
return text.substring(0, 120) + "...";
};
const computedFields: ComputedFields = {
summary: {
type: "string",
resolve: (doc) => {
return getSummarizedText(doc.body.raw);
},
},
};
const Article = defineDocumentType(() => ({
name: "Article",
filePathPattern: "**/*.md",
contentType: "markdown",
fields: {
title: { type: "string", required: true },
date: { type: "string", required: true },
basename: { type: "string", required: true },
categories: { type: "list", default: [], of: { type: "string" } },
},
computedFields,
}));
const Redirect = defineNestedType(() => ({
name: "Redirect",
fields: {
from: { type: "string", required: true },
to: { type: "string", required: true },
},
}));
const Redirects = defineDocumentType(() => ({
name: "Redirects",
filePathPattern: "redirects.json",
contentType: "data",
fields: {
redirects: { type: "list", default: [], of: Redirect },
},
}));
const config = makeSource({
contentDirPath: "contents",
documentTypes: [Article, Redirects],
});
export default config;
defineDocumentType
でドキュメントの形式、 front-matter などの型、その他フィールドを定義します。
こうすることによって、あとで contentlayer
からデータを取得する際に、型が効くようになります。
computedFields
を使うことで、 Markdown などのファイル名や内容から、動的に中身を生成することも可能です。
今回の場合は、 description
用の summary
を動的に生成しています。
そして、 next.config.js
に以下の設定を追記します。
const { withContentLayer } = require("next-contentlayer");
module.exports = withContentLayer()({
// 元の Next.js の設定
// 多分 latest (0.1.x) のバグ
webpack: (config) => {
config.resolve.alias = {
...config.resolve.alias,
"contentlayer/generated": path.join(__dirname, ".contentlayer/generated"),
};
return config;
},
});
これで準備完了です。
最後に、取得部分は以下のようにします。
import { allArticles, allRedirects } from "contentlayer/generated";
import type { Article as Entry } from "contentlayer/generated";
const getStaticPaths: GetStaticPaths<PathParams> = async () => {
const paths = allArticles.map((w) => {
const [year, month, day, slug] = w.basename.split("/");
return {
params: {
slug: [year, month, day, slug] as [string, string, string, string],
},
};
});
return {
paths,
fallback: false,
};
};
このように、 all${name に指定した名前}
で簡単に中身を取得することが可能です。
ということで、 contentlayer の紹介でした。
ちなみに、このブログもこの記事が反映されたタイミングで、内部で fs を使って集めるのでは無く、 contentlayer を用いた生成に切り替わっています。