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