GatsbyでMDXを試したけど案の定ド嵌りした
MDXというmarkdownの拡張みたいなものがあるらしいので使ってみました。次のますだらの更新で適用できたらいいなぁって感じです。Markdown中にコンポーネントを書けたり、その値を別のコンポーネントに持って行ったりできるので、自由度が高いのが売りみたいです。Markdown拡張自体はGatsbyのプラグインでも似たようなことは出来るのですが、いまいちな感じでした。
それとは別に、markdown中のコンポーネント内部に数式を表示したい気持ちがあります。これが可能なら、例えば定理型環境や可換図式をmarkdownで記述できるので便利です。MDXで実現できるかは分かりませんが、やってみる価値はありそうです。
ということで始めた導入なんですが、ド嵌りしました。簡単に言うと、ドキュメント通りに作っても動きません。最終的にはなんとか表示まで漕ぎつけたのでここに載せときます。詳しい解説は、詳しい人がそのうち書いてくれると思います。
インストール
npm install gatsby-plugin-mdx @mdx-js/mdx@latest @mdx-js/react@latest
コード
- ページ全体のレイアウトを定める
article-page-layout.js
をsrc/components
以下に作る。 - ページのコンテンツとなる
.mdx
ファイルはcontent/posts
以下に置くことにする。
gatsby-config.js
module.exports = { plugins: [ { // Add support for *.mdx files in Gatsby resolve: `gatsby-plugin-mdx`, options: { extensions: [`.mdx`], defaultLayouts: { default: require.resolve(`./src/components/article-page-layout.js`) } }, }, { // Add a collection called "posts" that looks for files in "content/posts" resolve: `gatsby-source-filesystem`, options: { name: `posts`, path: `${__dirname}/content/posts/`, }, }, ], }
mdxtest.mdx
--- title: "MDXのテスト" author: "mathmathniconico" date: "2019-07-16" --- # Hello MDX! MDX IS FUCK.
gatsby-node.js
const path = require("path") const { createFilePath } = require("gatsby-source-filesystem") exports.onCreateNode = ({ node, actions, getNode }) => { const { createNodeField } = actions if (node.internal.type === "Mdx") { const value = createFilePath({ node, getNode }) // Extend another node. The new node field is placed under the 'fields' key on the extended node object. createNodeField({ // Name of the field adding name: "slug", // Individual MDX node node, // Generated value based on filepath. value: `/blog${value}`, }) } } exports.createPages = ({ actions, graphql }) => { const { createPage } = actions return graphql(` { allMdx { edges { node { id fields { slug } } } } } `).then(result => { // Handling Errors. if (result.errors) { return Promise.reject(result.errors) } // Create pages. result.data.allMdx.edges.forEach(({ node }) => { createPage({ path: node.fields.slug, component: path.resolve(`./src/components/article-page-layout.js`), context: { // We can use the values in this context in our page layout component. id: node.id }, }) }) }) } exports.createSchemaCustomization = ({ actions }) => { const { createTypes } = actions const typeDefs = ` type Mdx implements Node { frontmatter: MdxFrontmatter } type MdxFrontmatter { author: String! date: Date tags: [String!]! } ` createTypes(typeDefs); }
article-page-layout.js
import React from 'react' import { graphql } from 'gatsby' import MDXRenderer from "gatsby-plugin-mdx/mdx-renderer" export default function pageTemplate({ data: { mdx } }) { return ( <div> <h1>{mdx.frontmatter.title}</h1> <MDXRenderer>{mdx.body}</MDXRenderer> <h2>{mdx.frontmatter.author}</h2> </div> ) } export const pageQuery = graphql` query BlogPostQuery($id: String) { mdx(id: { eq: $id }) { id body frontmatter { title author date(formatString: "MMMM DD, YYYY") } } } `
解説
とりあえず動いたってだけです。だいたいドキュメント通りに作ったのですが、嵌った理由は、mdx.bodyがドキュメントではmdx.code.bodyになってることです。仕様変更があったのでしょう。修正がプルリクに上がっていました。あとMdxのFrontmatterは最低限の機能しかないので、createSchemaCustomization
で追加する必要があります。
GraphQLの動きが良く分からない。引数とかpageQuery
で指定した形に持ってくる、とかそういう感じなんでしょうか?
なお肝心のKaTeXが追加できるかは知りません。gatsby-remark-katex
は動かないっぽいので、remark-math
とrehype-katex
を併用する形になると思いますが。
17日追記:できました。フォントを入れ忘れてただけでした。しかしコンポーネント内部に入れることはやはりできないので、残念です。