-
-
Notifications
You must be signed in to change notification settings - Fork 24
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement Gatsby
onCreateNode
and createPages
APIs
To integrate MDX Gatsby's `onCreateNode` () and `createPages` (2) APIs has been implemented to handle the generation of the GraphQL MDX nodes and the subsequent automated creation of the docs pages and blog posts. References: (1) https://www.gatsbyjs.org/docs/node-apis/#onCreateNode (2) https://www.gatsbyjs.org/docs/node-apis/#createPages GH-129
- Loading branch information
1 parent
a2ccb71
commit 1882e6b
Showing
10 changed files
with
241 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
/* | ||
* Copyright (C) 2018-present Arctic Ice Studio <[email protected]> | ||
* Copyright (C) 2018-present Sven Greb <[email protected]> | ||
* | ||
* Project: Nord Docs | ||
* Repository: https://github.com/arcticicestudio/nord-docs | ||
* License: MIT | ||
*/ | ||
|
||
const { resolve: r } = require("path"); | ||
|
||
const { nodeFields } = require("../src/config/internal/nodes"); | ||
const { ROUTE_BLOG, ROUTE_DOCS, ROUTE_LANDING, ROUTE_ROOT } = require("../src/config/routes/mappings"); | ||
|
||
const TemplateBlogPost = r(__dirname, "../src/components/templates/blog/BlogPost.jsx"); | ||
const TemplateDocsPage = r(__dirname, "../src/components/templates/docs/DocsPage.jsx"); | ||
|
||
const mdxQuery = ` | ||
{ | ||
allMdx { | ||
edges { | ||
node { | ||
code { | ||
scope | ||
} | ||
fields { | ||
${Object.keys(nodeFields).map(nf => nf.replace(",", "\n"))} | ||
} | ||
frontmatter { | ||
draft | ||
} | ||
id | ||
} | ||
} | ||
} | ||
} | ||
`; | ||
|
||
/** | ||
* Implementation of the Gatsby Node "createPages" API which tells plugins to add pages. | ||
* This extension point is called only after the initial sourcing and transformation of nodes and | ||
* when the creation of the GraphQL schema is complete to allow to query data in order to create pages. | ||
* | ||
* @author Arctic Ice Studio <[email protected]> | ||
* @author Sven Greb <[email protected]> | ||
* @since 0.10.0 | ||
* @see https://next.gatsbyjs.org/docs/node-apis/#createPages | ||
* @see https://next.gatsbyjs.org/docs/node-apis/#createPage | ||
* @see https://github.com/ChristopherBiscardi/gatsby-mdx | ||
*/ | ||
const createPages = async ({ graphql, actions }) => { | ||
const { createPage, createRedirect } = actions; | ||
const isProductionMode = process.env.NODE_ENV === "production"; | ||
|
||
/* Always redirect from the landing page to the root route. */ | ||
createRedirect({ | ||
fromPath: ROUTE_LANDING, | ||
redirectInBrowser: true, | ||
toPath: ROUTE_ROOT | ||
}); | ||
|
||
const mdxQueryResult = await graphql(mdxQuery); | ||
if (mdxQueryResult.errors) { | ||
throw Error("Error while running GraphQL query for MDX!", mdxQueryResult.errors); | ||
} | ||
|
||
mdxQueryResult.data.allMdx.edges.forEach(({ node }) => { | ||
const { id } = node; | ||
const { contentSourceType, date, relativeDirectory, slug, slugParentRoute } = node.fields; | ||
const { draft } = node.frontmatter; | ||
|
||
/* Only create non-draft pages in production mode while also create draft pages during development. */ | ||
if (draft && isProductionMode) return; | ||
|
||
let template; | ||
switch (slugParentRoute) { | ||
case ROUTE_BLOG: | ||
template = TemplateBlogPost; | ||
break; | ||
case ROUTE_DOCS: | ||
template = TemplateDocsPage; | ||
break; | ||
default: | ||
throw Error(`No matching template found while creating page for node with path ${slugParentRoute}${slug}!`); | ||
} | ||
|
||
createPage({ | ||
path: `${slugParentRoute}${slug}`, | ||
component: template, | ||
/* | ||
* Make the specified fields available as variables in page queries. | ||
* | ||
* @see https://graphql.org/learn/queries/#variables | ||
* @see https://www.gatsbyjs.org/docs/graphql-reference/#query-variables | ||
*/ | ||
context: { | ||
contentSourceType, | ||
date, | ||
id, | ||
relativeDirectory, | ||
slug, | ||
slugParentRoute | ||
} | ||
}); | ||
}); | ||
}; | ||
|
||
module.exports = createPages; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
/* | ||
* Copyright (C) 2018-present Arctic Ice Studio <[email protected]> | ||
* Copyright (C) 2018-present Sven Greb <[email protected]> | ||
* | ||
* Project: Nord Docs | ||
* Repository: https://github.com/arcticicestudio/nord-docs | ||
* License: MIT | ||
*/ | ||
|
||
const { createFilePath } = require("gatsby-source-filesystem"); | ||
|
||
const { nodeFields, sourceInstanceTypes } = require("../src/config/internal/nodes"); | ||
const { BASE_DIR_CONTENT, NODE_TYPE_MDX, REGEX_BLOG_POST_DATE } = require("../src/config/internal/constants"); | ||
const { ROUTE_BLOG, ROUTE_DOCS } = require("../src/config/routes/mappings"); | ||
|
||
/** | ||
* Extracts the date of a blog post from the given path using the `REGEX_BLOG_POST_DATE` regular expression. | ||
* Note that the returned date is in UTC format to be independent of the time zone. The exact time of the day will be | ||
* parsed from the blog posts frontmatter "publishTime" field. | ||
* | ||
* @private | ||
* @method extractDateFromPath | ||
* @param {string} path The path from which the blog post date should be extracted. | ||
* @return {string|null} The extracted blog post date in UTC format as JSON string if the given path matches the | ||
* regular expression, `null` otherwise. | ||
*/ | ||
const extractBlogPostDateFromPath = path => { | ||
const date = REGEX_BLOG_POST_DATE.exec(path); | ||
return date ? new Date(Date.UTC(date[1], date[2] - 1, date[3])).toJSON() : null; | ||
}; | ||
|
||
/** | ||
* Implementation of the Gatsby Node "onCreateNode" API which gets called when a new node is created. | ||
* Allows plugins to extend or transform nodes created by other plugins. | ||
* | ||
* @author Arctic Ice Studio <[email protected]> | ||
* @author Sven Greb <[email protected]> | ||
* @since 0.10.0 | ||
* @see https://next.gatsbyjs.org/docs/node-apis/#onCreateNode | ||
* @see https://next.gatsbyjs.org/docs/actions/#createNode | ||
* @see https://next.gatsbyjs.org/docs/actions/#createNodeField | ||
* @see https://github.com/gatsbyjs/gatsby/tree/master/packages/gatsby-source-filesystem#createfilepath | ||
*/ | ||
const onCreateNode = ({ node, getNode, actions }) => { | ||
const { createNodeField } = actions; | ||
if (node.internal.type === NODE_TYPE_MDX) { | ||
const contentFileResourceSlug = createFilePath({ | ||
node, | ||
getNode, | ||
basePath: `${BASE_DIR_CONTENT}`, | ||
trailingSlash: false | ||
}); | ||
const { relativeDirectory, relativePath, sourceInstanceName } = getNode(node.parent); | ||
|
||
if (sourceInstanceName === sourceInstanceTypes.blog.id) { | ||
const date = extractBlogPostDateFromPath(relativePath); | ||
|
||
if (!date) { | ||
throw Error( | ||
`Blog post content resource path '${relativePath}' doesn't match the required date-based directory structure: ${REGEX_BLOG_POST_DATE}` | ||
); | ||
} | ||
|
||
createNodeField({ | ||
node, | ||
name: `${nodeFields.date.name}`, | ||
value: extractBlogPostDateFromPath(relativePath) | ||
}); | ||
createNodeField({ | ||
node, | ||
name: `${nodeFields.contentSourceType.name}`, | ||
value: `${sourceInstanceTypes.blog.id}` | ||
}); | ||
createNodeField({ | ||
node, | ||
name: `${nodeFields.relativeDirectory.name}`, | ||
value: `${relativeDirectory}` | ||
}); | ||
createNodeField({ | ||
node, | ||
name: `${nodeFields.slug.name}`, | ||
value: `${contentFileResourceSlug}` | ||
}); | ||
createNodeField({ | ||
node, | ||
name: `${nodeFields.slugParentRoute.name}`, | ||
value: `${ROUTE_BLOG}` | ||
}); | ||
} | ||
|
||
if (sourceInstanceName === sourceInstanceTypes.docs.id) { | ||
createNodeField({ | ||
node, | ||
name: `${nodeFields.contentSourceType.name}`, | ||
value: `${sourceInstanceTypes.docs.id}` | ||
}); | ||
createNodeField({ | ||
node, | ||
name: `${nodeFields.relativeDirectory.name}`, | ||
value: `${relativeDirectory}` | ||
}); | ||
createNodeField({ | ||
node, | ||
name: `${nodeFields.slug.name}`, | ||
value: contentFileResourceSlug | ||
}); | ||
createNodeField({ | ||
node, | ||
name: `${nodeFields.slugParentRoute.name}`, | ||
value: `${ROUTE_DOCS}` | ||
}); | ||
} | ||
} | ||
}; | ||
|
||
module.exports = onCreateNode; |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
export const frontmatter = { | ||
contentImages: ["./placeholder.png"], | ||
title: "GraphQL query error draft stub", | ||
introduction: "This file must be kept to prevent GraphQL queries to fail when no blog posts are in the filesystem.", | ||
heroImage: "./placeholder.png", | ||
publishTime: "00:00:00+0000", | ||
draft: true | ||
}; |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
export const frontmatter = { | ||
contentImages: ["./placeholder.png"], | ||
title: "GraphQL query error draft stub", | ||
subline: "This file must be kept to prevent GraphQL queries to fail when no docs pages are in the filesystem.", | ||
draft: true | ||
}; |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters