Skip to content

Commit

Permalink
Merge pull request #130 from arcticicestudio/feature/gh-129-mdx-integ…
Browse files Browse the repository at this point in the history
…ration-docs-blog

MDX Integration for docs and blog pages
  • Loading branch information
arcticicestudio authored Mar 9, 2019
2 parents d71e4f9 + 6d48c68 commit 05d3211
Show file tree
Hide file tree
Showing 148 changed files with 5,536 additions and 241 deletions.
109 changes: 109 additions & 0 deletions .gatsby/createPages.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
/*
* 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, publishTime } = 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,
publishTime,
relativeDirectory,
slug,
slugParentRoute
}
});
});
};

module.exports = createPages;
2 changes: 1 addition & 1 deletion .gatsby/onCreateBabelConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
* @since 0.1.0
*/
const onCreateBabelConfig = ({ actions }) => {
const isProductionMode = () => process.env.NODE_ENV === "production";
const isProductionMode = process.env.NODE_ENV === "production";

/*
* Allows to use the "ES Class Fields & Static Properties" proposal to transforms static class properties as well as
Expand Down
168 changes: 168 additions & 0 deletions .gatsby/onCreateNode.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
/*
* 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 glob = require("glob");
const { createFilePath } = require("gatsby-source-filesystem");
const { dirname } = require("path");
const { existsSync } = require("fs");

const {
nodeFields,
optionalBlogPostImages,
optionalBlogPostVideos,
sourceInstanceTypes,
requiredBlogPostImages
} = 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.
* The exact time of the day will be parsed from the blog posts frontmatter "publishTime" field.
*
* @private
* @method extractBlogPostDateFromPath
* @param {string} path The path from which the blog post date should be extracted.
* @return {string|null} The extracted blog post date if the given path matches the regular expression,
* `null` otherwise.
*/
const extractBlogPostDateFromPath = path => {
const date = REGEX_BLOG_POST_DATE.exec(path);
return date ? `${date[1]}-${date[2]}-${date[3]}` : 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 { absolutePath, 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}`
);
}

/* Check for required blog post images and generate node fields. */
Object.keys(requiredBlogPostImages).forEach(image => {
const { name, nodeFieldName } = requiredBlogPostImages[image];
const matches = glob.sync(`${dirname(absolutePath)}/${name}.?(png|jpg|jpeg)`);

if (!matches.length) {
throw Error(`Required '${name}.(png|jpg|jpeg)' image not found for blog post '${relativeDirectory}'!`);
}
if (existsSync(matches[0])) {
createNodeField({
node,
name: nodeFieldName,
value: matches[0]
});
}
});

/* Check for optional blog post images and generate node fields. */
Object.keys(optionalBlogPostImages).forEach(image => {
const { name, nodeFieldName } = optionalBlogPostImages[image];
const matches = glob.sync(`${dirname(absolutePath)}/${name}.?(png|jpg|jpeg)`);

if (existsSync(matches[0])) {
createNodeField({
node,
name: nodeFieldName,
value: matches[0]
});
}
});

/* Check for optional blog post videos and generate node fields. */
Object.keys(optionalBlogPostVideos).forEach(video => {
const { name, nodeFieldName } = optionalBlogPostVideos[video];
const matches = glob.sync(`${dirname(absolutePath)}/${name}.?(mp4|webm)`);
if (existsSync(matches[0])) {
createNodeField({
node,
name: nodeFieldName,
value: matches[0]
});
}
});

createNodeField({
node,
name: `${nodeFields.date.name}`,
value: date
});
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;
39 changes: 39 additions & 0 deletions .gatsby/plugins/mdx.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* 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
*/

/**
* @file The configuration for the Gatsby plugin `gatsby-mdx`.
* @author Arctic Ice Studio <[email protected]>
* @author Sven Greb <[email protected]>
* @since 0.10.0
* @see https://mdxjs.com
* @see https://github.com/ChristopherBiscardi/gatsby-mdx
*/

const remarkBreaks = require("remark-breaks");
const remarkGitHub = require("remark-github");
const rehypeSlug = require("rehype-slug");

const { metadataNordDocs } = require("../../src/data/project");

const remarkGitHubOptions = {
repository: `${metadataNordDocs.repository.url}`,
mentionStrong: false
};

module.exports = {
defaultLayouts: {
blog: require.resolve("../../src/components/templates/blog/BlogPost.jsx"),
docs: require.resolve("../../src/components/templates/docs/DocsPage.jsx")
},
footnotes: true,
gatsbyRemarkPlugins: [],
hastPlugins: [rehypeSlug],
mdPlugins: [remarkBreaks, [remarkGitHub, remarkGitHubOptions]]
};
43 changes: 43 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* 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
*/

/**
* The Visual Studio Code debug configuration for Gatsby.
* @author Arctic Ice Studio <[email protected]>
* @author Sven Greb <[email protected]>
* @since 0.10.0
* @see https://github.com/Microsoft/vscode-recipes/tree/master/Gatsby-js
* @see https://www.gatsbyjs.org/docs/debugging-the-build-process
*/
{
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Gatsby Development Build Process",
"program": "${workspaceRoot}/node_modules/gatsby/dist/bin/gatsby",
"args": ["develop"],
"runtimeArgs": [
/*
* For performance reasons Node.js parses functions lazily on first access.
* As a consequence, breakpoints don't work in source code areas that haven't been parsed by Node.js.
* The `--nolazy` flag prevents the delayed parsing and ensures that breakpoints can be validated before
* running the code.
*
* @see https://nodejs.org/api/cli.html#cli_v8_options
*/
"--nolazy"
],
"stopOnEntry": false,
"sourceMaps": false,
"internalConsoleOptions": "openOnSessionStart"
}
]
}
1 change: 0 additions & 1 deletion content/blog/.gitkeep

This file was deleted.

Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Empty file.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 7 additions & 0 deletions content/blog/2019/03/02/query-error-draft-stub/index.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export const frontmatter = {
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.",
publishTime: "00:00:00+0000",
coverTitleColor: "",
draft: true
};
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 0 additions & 1 deletion content/docs/.gitkeep

This file was deleted.

5 changes: 5 additions & 0 deletions content/docs/query-error-draft-stub/index.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export const frontmatter = {
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
};
Binary file added content/docs/query-error-draft-stub/placeholder.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 05d3211

Please sign in to comment.