導入
突然ですが、皆さんはMDXを使っていますか?
MDXとは、Markdown(マークダウン)にjsxのコンポーネントが埋め込めるもので、Reactを使っているとReactのコンポーネントを使いまわすことができるので、非常に便利です。
Gatsby.jsでも当然MDXは使えますが、有名なブログテンプレートであるgatsby-starter-blogではそのままの設定で使うことはできず、追加の設定が必要です。
そこで今回は、Gatsby.js製のブログで既にMarkdownを使っている場合に、MDX (MDX2) に移行する方法を紹介します。
移行時につまづいたポイントについても紹介するので、参考にしていただけると嬉しいです。
スポンサードリンク
MDXとは
MDXへの移行方法を説明する前に、まずMDXについて説明します。
MDX(mdx.js)とは、以下のようなMarkdownにjsxを組み合せた形式のことです。
import { Chart } from './chartComponent.js';export const year = new Date().getFullYear(); // 今年の西暦
# 今年は何年?今年は {year} 年です。 このように、**JavaScriptを使って変数を埋め込む**ことができます。 また、以下のようにjsxのコンポーネントを埋め込むことができます。
<Chart size="middle" year={year} />
<div className="center"> <img src="./test.jpg" /></div>
特徴としては、以下のものがあります。
- Markdownの構文が使える
- jsxのコンポーネントをimportし、利用することができる
- exportの後に変数を定義し、利用することができる
このため、jsxを用いるReactと非常に相性が良いです。
MDXを導入することで、Reactでコンポーネントを作成し、MDX上で繰り返し利用することができます。
バージョンによる違い
2023年現在、MDXには大きく分けて2つのバージョンがあります。
1つがバージョン1、もう1つが最新のバージョン2(MDX2)です。
バージョン1からバージョン2への変更では、コンパイルの高速化やコード実行速度の向上、バンドルサイズの低下など多くの点が改良されています。
今回の記事では最新のバージョン2(MDX2)に対応した移行方法を紹介します。
変換の仕組み
変換の仕組みについても、簡単に説明します。
MDXでは、最終的にjsxに変換されます。
通常はこれをブラウザ上でHTMLにレンダリングします。
Gatsby.jsでは、SSGによってビルド時にHTMLに変換されます。
MDXの変換の仕組みについての詳細は、以下のページを参照してください。
https://mdxjs.com/packages/mdx/#architecture
MarkdownからMDXに移行する方法
それでは本題のMarkdownからMDXに移行する方法について紹介します。
いくつか詰まるポイントがあるので、注意が必要です。
移行の手順は以下のステップになっています。
- 事前準備
- gatsby-plugin-mdxのインストール
- gatsby-transformer-remarkからgatsby-plugin-mdxへの置き換え
- gatsby-node.jsの更新
- 各ページ・テンプレートの修正
- MarkdownファイルのHTMLの置き換え
- コンソールエラーの修正 (Gatsby4以下の場合)
- (オプション) GitHub Flavored Markdown (GFM) の設定
- (オプション) RSSフィードの修正
- (オプション) gatsby-remark-prismjsからprism-react-rendererの置き換え
ステップ数は多いですが、説明通り進めればエラーなく進めることができると思います。
それでは、順に説明します。
スポンサードリンク
1. 事前準備
今回の手順を進めるにあたって、まずGatsby.jsのバージョンを確認する必要があります。
package.jsonを開き、dependencies内のgatsbyのバージョンが4.21.0以降になっていることを確認します。
"dependencies": { "gatsby": "^4.25.2",
もしバージョンが4.21.0よりも前の場合は、npmやyarnを使ってgatsbyのバージョンをアップデートします。
<npmの場合>
npm install gatsby@{アップデートしたいバージョン}
<yarnの場合>
yarn upgrade gatsby@{アップデートしたいバージョン}
また、gatsbyのバージョンをアップデートした場合は、gatsby-plugin-
から始まるすべてのパッケージも同様に、対応するバージョンにアップデートします。
以下は、gatsby-plugin-canonical-urls
の場合の例です。
<npmの場合>
npm install gatsby-plugin-canonical-urls@{アップデートしたいバージョン}
<yarnの場合>
yarn upgrade gatsby-plugin-canonical-urls@{アップデートしたいバージョン}
最後に、package.jsonにgatsby-transformer-remark
があるか確認します。
もしある場合は、事前にアンインストールしておきます。(gatsby-plugin-mdx
と競合してしまうため)
<npmの場合>
npm uninstall gatsby-transformer-remark
<yarnの場合>
yarn remove gatsby-transformer-remark
これで、事前準備は完了です。
2. gatsby-plugin-mdxのインストール
次に、gatsby-plugin-mdx
というプラグインをインストールします。
これはGatsby.jsでMDXを使えるようにするためのものです。
以下のコマンドを実行します。
<npmの場合>
npm install @mdx-js/react gatsby-plugin-mdx
<yarnの場合>
yarn add @mdx-js/react gatsby-plugin-mdx
3. gatsby-transformer-remarkからgatsby-plugin-mdxへの置き換え
次に、gatsby-config.js内の設定をgatsby-transformer-remarkからgatsby-plugin-mdxに置き換えます。
以下のように、-とある行をその下の+とある行に変更します。
{- resolve: `gatsby-transformer-remark`+ resolve: `gatsby-plugin-mdx` options: {- plugins: [+ gatsbyRemarkPlugins: [
さらに、拡張子を.md
から.md
と.mdx
に変更します。
これによって、.md
と.mdx
のファイル両方を変換することができます。
{ resolve: `gatsby-plugin-mdx`, options: { extensions: [`.md`, `.mdx`], //変更する行 }, },
4. gatsby-node.jsの更新
続いて、gatsby-node.js内のMarkdownRemarkをMdxに置き換えます。
それぞれ以下のように置き換えます。
gatsby-transformer-remark | => | gatsby-plugin-mdx |
---|---|---|
allMarkdownRemark | allMdx | |
MarkdownRemark | Mdx | |
Frontmatter | MdxFrontmatter |
具体的には、以下のように-の行を削除し、+の行を追加します。
+ const indexTemplate = path.resolve("src/pages/index.js")
// Get all markdown blog posts sorted by date const result = await graphql( ` {- postsRemark: allMarkdownRemark(+ postsRemark: allMdx( sort: { fields: [frontmatter___date], order: ASC } limit: 2000 ) { totalCount nodes { id fields { slug } frontmatter { tags }+ internal {+ contentFilePath+ } } }- tagsGroup: allMarkdownRemark(limit: 2000) {+ tagsGroup: allMdx(limit: 2000) {
for (let i = 0; i < pageCount; i++) { createPage({ path: i === 0 ? `/` : `/pages/${i + 1}/`,- component: path.resolve("src/pages/index.js"),+ component: indexTemplate, context: { index: i,
// Create blog posts pages // But only if there's at least one markdown file found at "content/blog" (defined in gatsby-config.js) // `context` is available in the template as a prop and as a variable in GraphQL if (posts.length > 0) { posts.forEach((post, index) => { const previousPostId = index === 0 ? null : posts[index - 1].id const nextPostId = index === posts.length - 1 ? null : posts[index + 1].id
createPage({ path: post.fields.slug,- component: blogPost,+ component: `${blogPost}?__contentFilePath=${post.internal.contentFilePath}`, context: { id: post.id, previousPostId,
exports.onCreateNode = ({ node, actions, getNode }) => { const { createNodeField } = actions
- if (node.internal.type === `MarkdownRemark`) {+ if (node.internal.type === `Mdx`) {
- type MarkdownRemark implements Node {- frontmatter: Frontmatter+ type Mdx implements Node {+ frontmatter: MdxFrontmatter
- type Frontmatter {+ type MdxFrontmatter {
これで、gatsby-node.jsの置き換えは完了です。
5. 各ページ・テンプレートの修正
src/pages/index.jsなどの各ページやsrc/templates/blog-post.js、src/templates/tags.jsなどの各テンプレートについても、以下のように置き換えます。
gatsby-transformer-remark | => | gatsby-plugin-mdx |
---|---|---|
allMarkdownRemark | allMdx | |
markdownRemark | mdx |
また、記事のテンプレートであるblog-post.jsのpropsにchildren
を追加します。
- const BlogPostTemplate = ({ data, location }) => {+ const BlogPostTemplate = ({ data, location, children }) => {
加えて、GraphQLの部分でhtml
フィールドを削除します。
export const pageQuery = graphql` query BlogPostBySlug( $id: String! $previousPostId: String $nextPostId: String ) { site { siteMetadata { title } } mdx(id: { eq: $id }) { id excerpt(pruneLength: 100)- html
そして、記事の本文の部分articleBody
を以下のように置き換えます。
- <section- dangerouslySetInnerHTML={{ __html: post.html }}- itemProp="articleBody"- />+ <section itemProp="articleBody">+ {children}+ </section>
最後に、GraphQLのexcerptにtruncateがある場合、削除しないと以下のエラーが出るため、削除します。
Unknown argument "truncate" on field "Mdx.excerpt".
- excerpt(truncate: true, pruneLength: 100)+ excerpt(pruneLength: 100)
6. MarkdownファイルのHTMLの置き換え
MDXではjsxを用いているため、Markdown内にHTMLを書き込んでいる場合は、jsxに直す必要があります。
置き換えずにビルドしようとすると、おそらく以下のようなエラーが出ます。
ここが一番つまづいたポイントでした。
ERROR
Module build failed (from ./node_modules/gatsby/dist/utils/babel-loader.js):SyntaxError: C:\Users\{UserName}\{Your path to repository}\content\blog\js-transform-image-web-animations-api\index.md: Invalid left-hand side in prefix operation. (1:2)
> 1 | --- | ^ 2 | title: "~"
主な変換ポイントは以下のようなものです。
HTML | => | jsx |
---|---|---|
class | className | |
<img src=”~” > | <img src=”~” /> | |
<br> | <br /> | |
< | \< | |
{ | \{ | |
style=“display:block” | style={{ display: ‘block’ }} |
特に、文字として<
や{
を使うときにエラーになってしまうのが落とし穴です。
この場合は必ずエスケープ文字\
を使って、\<
や\{
とします。
また、閉じタグに/
も必ず必要になります。
7. コンソールエラーの修正 (Gatsby4以下の場合)
(Gatsby5以降はgatsby-plugin-mdxにremark-unwrap-imagesが含まれるようになったので、ここにある手順は不要です)
package.jsonに書いてあるGatsbyのバージョンが4系(4.x.x)またはそれ未満の場合、ブラウザのコンソールに以下のようなエラーが出ていると思います。
Warning: validateDOMNesting(...): <div> cannot appear as a descendant of <p>
これは、<p>タグで画像部分の<div>タグを囲ってしまっていることが原因です。
Gatsbyの画像は<div>タグで囲われており、それをMDXの変換時に1行と認識され、<p>タグで囲われてしまっています。
これを解消するためには、まず以下のコマンドでremark-unwrap-imagesというパッケージをインストールします。
<npmの場合>
npm install remark-unwrap-images@2.1.0
<yarnの場合>
yarn add remark-unwrap-images@2.1.0
その後、gatsby-config.jsのmdxOptionsに以下のよ うに追加します。
{ resolve: `gatsby-plugin-mdx`, options: { extensions: [`.md`, `.mdx`], gatsbyRemarkPlugins: [ ... ],+ mdxOptions: {+ remarkPlugins: [+ // fix an error+ require(`remark-unwrap-images`),+ ]+ } }, },
これで、画像が<p>タグで囲われなくなり、エラーが発生しなくなったはずです。
8. (オプション) GitHub Flavored Markdown (GFM) の設定
上記の手順まででも最低限は動くのですが、このままだと表や取り消し線、自動リンクなどが使えないと思います。
実はこれは標準のMarkdownではなく、GitHubで使われているGitHub Flavored Markdown (GFM) というものです。
具体的には、以下のようなものです。
・表
|aaa|bbb|ccc||:---:|:---:|:---:||ddd|eee|fff|
変換後
aaa | bbb | ccc |
---|---|---|
ddd | eee | fff |
・取り消し線
~~取り消し内容~~
変換後
取り消し内容
・自動リンク
https://bel-itigo.com/
これらを使用したい場合は、まずremark-gfm
をインストールします。
ここで、gatsby-config.jsではまだimport文をサポートしていないため、require文を使うことができるバージョン1系をインストールする必要があります。
<npmの場合>
npm install remark-gfm@^1
<yarnの場合>
yarn add remark-gfm@^1
そして、gatsby-config.jsのmdxOptionsに以下のように追加します。
{ resolve: `gatsby-plugin-mdx`, options: { extensions: [`.md`, `.mdx`], gatsbyRemarkPlugins: [ ... ], mdxOptions: { remarkPlugins: [+ // Add GitHub Flavored Markdown (GFM) support+ require(`remark-gfm`), // fix an error require(`remark-unwrap-images`), ] } }, },
これで設定は完了です。
9. (オプション) RSSフィードの修正
ブログランキングサイトに登録している方など、RSSフィードを使用したい場合は、gatsby-config.jsを修正する必要があります。
詳しくは別の記事に方法を書いたので、気になる方は見てください。
10. (オプション) gatsby-remark-prismjsからprism-react-rendererの置き換え
最後に、シンタックスハイライトについて、gatsby-remark-prismjsとMDXの相性が良くない場合があります。
この場合には、gatsby-remark-prismjsからprism-react-rendererへ置き換えることをおすすめします。
このプラグインの置き換えについては長くなるので、別の記事で後日説明する予定です。
スポンサードリンク
まとめ
今回の記事では、Gatsby.js製のブログでMarkdownからMDXに置き換える方法を紹介しました。
私が途中で遭遇したエラーとその対処方法をすべて入れたため、非常に長くなってしまいましたが、その分充実した記事になったはずです。
MDXへの移行は大変でしたが、これでReactのコンポーネントが再利用できるの で、記事中でも様々なことが可能になります。
この記事が少しでも参考になった場合は、周りの方にシェアしていただけると嬉しいです。