Gatsby に RSS フィードを追加

2020/11/25

はじめに

Gatsby で構築したブログに RSS フィードを追加する場合、公式ドキュメントの通りにやればほぼ問題ないんですが、ちょっとだけ日本特有の追加対応をしたので、そのメモです。

対応内容

前提

  • pubDate には yyyy-mm-dd 形式の edge.node.frontmatter.date を利用。
  • custom_elements は使わず、description には edge.node.excerpt を利用。

日本特有の問題

pubDate 時差問題

何も考えずに公式ドキュメントの通りに edge.node.frontmatter.date をそのまま使うと、GMT として解釈されてしまいます。

例えば、date: "2020-11-25" の場合、下記の通りに出力されます。

<pubDate>Wed, 25 Nov 2020 00:00:00 GMT</pubDate>

例の如く、直接使っている gatsby-plugin-feed は Gatsby 用アダプタ的な位置付けで、実処理は他のライブラリに移譲しており、そちらのマニュアルを見ると、itemOptionsdateDate object or date string と説明があります。date string のフォーマットって何だろう・・・と、あまりピンと来なかったので、実コードを読んでみると、pubDate は new Date(item.date).toGMTString() で生成されているようでした。

上記を踏まえ、対応選択肢はいくつかありますが、正しい Date オブジェクトを渡す方針で進めます。具体的には Moment.js で正しく変換してあげます。

- date: edge.node.frontmatter.date,
+ date: moment(edge.node.frontmatter.date).toDate(),

これで下記の通り、意図した出力になりました。

<pubDate>Tue, 24 Nov 2020 15:00:00 GMT</pubDate>

excerpt 問題

gatsby-transformer-remark のドキュメントに記載されている通り、文字数ベースで excerpt を利用する際、日本語使うなら truncate オプションを使いましょう。そうでないと変な所でバッサリ切られてしまいます。

実対応

ライブラリ追加

yarn add gatsby-plugin-feed moment

gatsby-config.js 差分

+const moment = require('moment');
+
 module.exports = {
   siteMetadata: {
     title: 'リアキテク・ラボ Tech Blog',
@@ -47,5 +50,42 @@ module.exports = {
     },
     'gatsby-plugin-react-helmet',
     'gatsby-plugin-sitemap',
+    {
+      resolve: 'gatsby-plugin-feed',
+      options: {
+        feeds: [
+          {
+            serialize: (
+              { query: { site, allMarkdownRemark } },
+            ) => allMarkdownRemark.edges.map((edge) => ({
+              ...edge.node.frontmatter,
+              description: edge.node.excerpt,
+              date: moment(edge.node.frontmatter.date).toDate(),
+              url: site.siteMetadata.siteUrl + edge.node.fields.slug,
+              guid: site.siteMetadata.siteUrl + edge.node.fields.slug,
+            })),
+            query: `
+            {
+              allMarkdownRemark(
+                sort: { order: DESC, fields: [frontmatter___date] },
+              ) {
+                edges {
+                  node {
+                    excerpt(truncate: true, pruneLength: 280)
+                    html
+                    fields { slug }
+                    frontmatter {
+                      title
+                      date
+                    }
+                  }
+                }
+              }
+            }`,
+            output: '/rss.xml',
+          },
+        ],
+      },
+    },
   ],
 };

おわりに

時差問題。日本語処理問題。ありがちです。

あと、本題とは全然関係ないんですが、Date オブジェクトをコンストラクタ引数として渡すと同じ Date オブジェクトが複製されるんですね・・・。勉強になりました。