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 用アダプタ的な位置付けで、実処理は他のライブラリに移譲しており、そちらのマニュアルを見ると、itemOptions の date は Date 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 オブジェクトが複製されるんですね・・・。勉強になりました。