Markdown による記事投稿
このサイトを始めるにあたり、記事は Markdown 記法で書きたかったので gatsby-plugin-mdx を導入した。Markdown 記法が使えればよかったのでチュートリアルにも出てきた gatsby-transformer-remark を使おうと考えていたが、MDX を使うと React コンポーネントを挿入できるということだったので優位性を感じ導入した。一部省略しているが下記のようなプラグイン設定を行っている。
gatsby-config.js
{
plugins: [
{
resolve: `gatsby-plugin-mdx`,
options: {
defaultLayouts: {
posts: require.resolve("./src/layouts/post-layout.js"),
},
gatsbyRemarkPlugins: [
{
resolve: `gatsby-remark-images`,
options: {
maxWidth: 500,
},
},
],
},
},
]
gatsby-node.js の方も一部省略だが下記のようにして、記事ページに URL を持たせた。
gatsby-node.js
const path = require(`path`)
const { createFilePath } = require(`gatsby-source-filesystem`)
exports.onCreateNode = ({ node, getNode, actions }) => {
const { createNodeField } = actions
if (node.internal.type === `Mdx`) {
const fileNode = getNode(node.parent)
createNodeField({
node,
name: `modifiedTime`,
value: fileNode.modifiedTime,
})
createNodeField({
node,
name: `birthTime`,
value: fileNode.birthTime,
})
const slug = createFilePath({ node, getNode, basePath: `posts` })
createNodeField({
node,
name: `slug`,
value: slug,
})
}
}
exports.createPages = async ({ graphql, actions }) => {
const { createPage } = actions
await createPostPages(graphql, createPage)
}
async function createPostPages(graphql, createPage) {
const result = await graphql(`
{
allMdx {
edges {
node {
fields {
slug
}
}
}
}
}
`)
result.data.allMdx.edges.forEach(( {node} ) => {
createPage({
path: node.fields.slug,
component: path.resolve(`./src/layouts/post-layout.js`),
context: {
slug: node.fields.slug,
}
})
})
}
コードブロック
コードブロックには「言語ごとのハイライト・タイトル表示・行番号表示」が欲しかったので prism-react-renderer を導入し、更にコードブロック用のコンポーネントを作成した。
src/components/codeblock.js
import React from 'react'
import Highlight, {defaultProps} from 'prism-react-renderer'
import theme from "prism-react-renderer/themes/oceanicNext";
import * as styles from "./codeblock.module.css"
export default function CodeBlock({children, className}) {
let [language, title] = (className || '').split(':');
language = language.replace(/language-/, '')
const CodeTitle = () => {
if (title) {
return (
<div className={styles.codeTitle}>
<span>{title}</span>
</div>
)
}
return (
<span></span>
)
}
return (
<Highlight {...defaultProps} theme={theme} code={children} language={language}>
{({ className, style, tokens, getLineProps, getTokenProps }) => {
tokens.pop()
return (
<div className={styles.codeBlockRoot}>
<CodeTitle />
<pre className={`${styles.codePre} ${className}`} style={style}>
{tokens.map((line, i) => {
const {
style: s,
className: c,
} = getLineProps({ line, key: i })
return (
<div key={i} style={s} className={`${styles.codeLine} ${c}`}>
<span className={styles.codeLineNumber}>{i + 1}</span>
<span className={styles.codeLineContent}>
{line.map((token, key) => (
<span key={key} {...getTokenProps({ token, key })} />
))}
</span>
</div>
)
})}
</pre>
</div>
)
}}
</Highlight>
)
}
そしたら今度はそのコンポーネントを MDX ファイルの中で使えるようにレイアウト側を編集する。こうすることでようやく Markdown のコードブロック記法で、ハイライト・タイトル・行番号の三拍子揃った表示ができるようになった。
src/layouts/posts-layout.js
import React from "react"
import { Link, graphql } from "gatsby"
const components = {
pre: props => <div {...props} />,
code: CodeBlock,
Link,
}
export default function PostLayout ({ data }) {
return (
<MDXProvider
components={components}
>
<MDXRenderer>{data.mdx.body}</MDXRenderer>
</MDXProvider>
)
}
export const query = graphql`
query($slug: String!) {
mdx( fields: { slug: { eq: $slug } }) {
body
}
}
`