Skip to content

Commit fb77241

Browse files
author
Jonathan Steele
authored
feat: Build markdown nodes from CMS RichText fields (#98)
* feat: Build markdown nodes for CMS markdown fields Can be used with MDX * feat: Add buildMarkdownNodes configuration option Defaults to false * style: Linting * chore(demo): Add MDX dependencies * chore(demo): Update demo to use MDX * fix: Remove optional chaining Eventually transpile with Babel * docs: Update README to include markdownNode usage * docs: Add link to demo source For a full MDX example
1 parent f820c81 commit fb77241

10 files changed

Lines changed: 1020 additions & 45 deletions

File tree

demo/gatsby-browser.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
11
import React from 'react'
2+
import { MDXProvider } from '@mdx-js/react'
23

34
import Layout from './src/components/layout'
5+
import Parargraph from './src/components/paragraph'
46

57
import './src/styles/main.css'
68

79
const wrapPageElement = ({ element, props }) => {
810
return <Layout {...props}>{element}</Layout>
911
}
1012

11-
export { wrapPageElement }
13+
const wrapRootElement = ({ element }) => {
14+
return <MDXProvider components={{ p: Parargraph }}>{element}</MDXProvider>
15+
}
16+
17+
export { wrapPageElement, wrapRootElement }

demo/gatsby-config.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,13 @@ require('dotenv').config()
22

33
module.exports = {
44
plugins: [
5+
'gatsby-plugin-mdx',
56
'gatsby-plugin-postcss',
67
'gatsby-plugin-sharp',
78
{
89
resolve: 'gatsby-source-graphcms',
910
options: {
11+
buildMarkdownNodes: true,
1012
downloadLocalImages: true,
1113
endpoint: process.env.GRAPHCMS_ENDPOINT,
1214
token: process.env.GRAPHCMS_TOKEN,

demo/gatsby-node.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,11 @@ exports.createPages = async ({ actions: { createPage }, graphql }) => {
66
products: allGraphCmsProduct {
77
nodes {
88
description {
9-
text
9+
markdownNode {
10+
childMdx {
11+
body
12+
}
13+
}
1014
}
1115
formattedPrice
1216
id

demo/gatsby-ssr.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
export { wrapPageElement } from './gatsby-browser'
1+
export { wrapPageElement, wrapRootElement } from './gatsby-browser'

demo/package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,11 @@
88
"dev": "ENABLE_GATSBY_REFRESH_ENDPOINT=true gatsby develop"
99
},
1010
"dependencies": {
11+
"@mdx-js/mdx": "1.6.16",
12+
"@mdx-js/react": "1.6.16",
1113
"gatsby": "2.23.22",
1214
"gatsby-image": "2.4.13",
15+
"gatsby-plugin-mdx": "1.2.30",
1316
"gatsby-plugin-postcss": "2.3.11",
1417
"gatsby-plugin-sharp": "2.6.19",
1518
"gatsby-source-graphcms": "2.0.0",

demo/src/components/paragraph.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import React from 'react'
2+
3+
function Paragraph(props) {
4+
return <p className="leading-relaxed md:text-xl text-lg" {...props} />
5+
}
6+
7+
export default Paragraph

demo/src/templates/product-page.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import React from 'react'
22
import { graphql } from 'gatsby'
33
import Img from 'gatsby-image'
4+
import { MDXRenderer } from 'gatsby-plugin-mdx'
45

56
const ProductPage = ({ data: { productImages }, pageContext: { product } }) => {
67
const [mainImage] = productImages.nodes
@@ -17,9 +18,9 @@ const ProductPage = ({ data: { productImages }, pageContext: { product } }) => {
1718
{product.description && (
1819
<React.Fragment>
1920
<hr className="my-4" />
20-
<p className="leading-relaxed md:text-xl text-lg">
21-
{product.description.text}
22-
</p>
21+
<MDXRenderer>
22+
{product.description.markdownNode.childMdx.body}
23+
</MDXRenderer>
2324
</React.Fragment>
2425
)}
2526
</div>

gatsby-source-graphcms/README.md

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,10 @@ module.exports = {
6565

6666
- Download and cache GraphCMS image assets in your Gatsby project. [Learn more](#downloading-local-image-assets).
6767

68+
- `buldMarkdownNodes` _Boolean_ (default value: `false`)
69+
70+
- Build markdown nodes for all [`RichText`](https://graphcms.com/docs/reference/fields/rich-text) fields in your GraphCMS schema. [Learn more](#using-markdown-nodes).
71+
6872
## Downloading local image assets
6973

7074
This source plugin provides the option to download and cache GraphCMS assets in your Gatsby project. This enables you to use [`gatsby-image`](https://www.gatsbyjs.org/packages/gatsby-image), for image loading optimizations, with your GraphCMS image assets.
@@ -107,3 +111,51 @@ You can then use the fragments from [`gatsby-transformer-sharp`](https://www.gat
107111
```
108112

109113
For more information on using `gatsby-image`, please see the [documentation](https://www.gatsbyjs.org/packages/gatsby-image/?=#how-to-use).
114+
115+
## Using markdown nodes
116+
117+
This source plugin provides the option to build markdown nodes for all `RichText` fields in your GraphCMS schema, which in turn can be used with [MDX](https://mdxjs.com).
118+
119+
To enable this, add `buildMarkdownNodes: true` to your plugin configuration.
120+
121+
```js
122+
// gatsby-config.js
123+
module.exports = {
124+
plugins: [
125+
{
126+
resolve: 'gatsby-source-graphcms',
127+
options: {
128+
endpoint: process.env.GRAPHCMS_ENDPOINT,
129+
buildMarkdownNodes: true,
130+
},
131+
},
132+
],
133+
}
134+
```
135+
136+
Enabling this option adds a `markdownNode` nested field to all `RichText` fields on the generated Gatsby schema.
137+
138+
### Usage with `gatsby-plugin-mdx`
139+
140+
These newly built nodes can be used with [`gatsby-plugin-mdx`](https://www.gatsbyjs.org/packages/gatsby-plugin-mdx) to render markdown from GraphCMS.
141+
142+
Once installed, you will be able to query for `MDX` fields using a query similar to the one below.
143+
144+
```gql
145+
{
146+
allGraphCmsPost {
147+
nodes {
148+
id
149+
content {
150+
markdownNode {
151+
childMdx {
152+
body
153+
}
154+
}
155+
}
156+
}
157+
}
158+
}
159+
```
160+
161+
Check out the [demo source](https://github.com/GraphCMS/gatsby-source-graphcms/tree/next/demo) for an example of a full MDX implementation.

gatsby-source-graphcms/gatsby-node.js

Lines changed: 45 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
const crypto = require('crypto')
12
const {
23
wrapQueryExecutorWithQueue,
34
loadSchema,
@@ -116,7 +117,7 @@ exports.sourceNodes = async (gatsbyApi, pluginOptions) => {
116117

117118
exports.onCreateNode = async (
118119
{ node, actions: { createNode }, createNodeId, getCache },
119-
{ downloadLocalImages = false }
120+
{ buildMarkdownNodes = false, downloadLocalImages = false }
120121
) => {
121122
if (
122123
downloadLocalImages &&
@@ -137,16 +138,54 @@ exports.onCreateNode = async (
137138
console.error('gatsby-source-graphcms:', e)
138139
}
139140
}
141+
142+
if (buildMarkdownNodes) {
143+
const fields = Object.entries(node)
144+
.map(([, value]) => value)
145+
.filter(
146+
(value) =>
147+
value && value.remoteTypeName && value.remoteTypeName === 'RichText'
148+
)
149+
150+
if (fields.length) {
151+
fields.forEach((field) => {
152+
const markdownNode = {
153+
id: `MarkdownNode:${createNodeId(node.id)}`,
154+
parent: node.id,
155+
internal: {
156+
type: `GraphCMS_MarkdownNode`,
157+
mediaType: 'text/markdown',
158+
content: field.markdown,
159+
contentDigest: crypto
160+
.createHash(`md5`)
161+
.update(field.markdown)
162+
.digest(`hex`),
163+
},
164+
}
165+
166+
createNode(markdownNode)
167+
168+
field.markdownNode = markdownNode.id
169+
})
170+
}
171+
}
140172
}
141173

142174
exports.createSchemaCustomization = (
143175
{ actions: { createTypes } },
144-
{ downloadLocalImages = false }
176+
{ buildMarkdownNodes = false, downloadLocalImages = false }
145177
) => {
146178
if (downloadLocalImages)
147179
createTypes(`
148-
type GraphCMS_Asset {
149-
localFile: File @link
150-
}
151-
`)
180+
type GraphCMS_Asset {
181+
localFile: File @link
182+
}
183+
`)
184+
185+
if (buildMarkdownNodes)
186+
createTypes(`
187+
type GraphCMS_RichText {
188+
markdownNode: GraphCMS_MarkdownNode @link
189+
}
190+
`)
152191
}

0 commit comments

Comments
 (0)