Skip to content

Instantly share code, notes, and snippets.

@resir014
Last active June 7, 2018 18:58
Show Gist options
  • Save resir014/0448be2244a45aca5b55d0abdab3b0c4 to your computer and use it in GitHub Desktop.
Save resir014/0448be2244a45aca5b55d0abdab3b0c4 to your computer and use it in GitHub Desktop.
Simulate Jekyll's page generation on Gatsby!
// @ts-check
// Requires module: slug, gatsby-source-filesystem
const path = require('path')
const slugify = require('slug')
const { createFilePath } = require('gatsby-source-filesystem')
// Regex to parse date and title from the filename
const BLOG_POST_SLUG_REGEX = /^\/blog\/([\d]{4})-([\d]{2})-([\d]{2})-(.+)\/$/
exports.onCreateNode = ({ node, getNode, boundActionCreators }) => {
const { createNodeField } = boundActionCreators
if (node.internal.type === `MarkdownRemark`) {
const { permalink, redirect_from, category, layout } = node.frontmatter
const relativePath = createFilePath({ node, getNode, basePath: 'pages' })
let slug = permalink;
if (!slug && relativePath.includes('blog')) {
// Generate final path + graphql fields for blog posts
const match = BLOG_POST_SLUG_REGEX.exec(relativePath)
const year = match[1]
const month = match[2]
const day = match[3]
const filename = match[4]
slug = `/blog/${year}/${month}/${day}/${slugify(filename)}/`
const date = new Date(Number.parseInt(year), Number.parseInt(month) - 1, Number.parseInt(day))
// Blog posts are sorted by date and display the date in their header.
createNodeField({
node,
name: 'date',
value: date.toJSON()
})
}
if (!slug) {
slug = relativePath
}
console.log(`success Generated slug: ${slug}`)
// Used to generate URL to view this content.
createNodeField({
node,
name: `slug`,
value: slug
})
// Used to determine a page layout.
createNodeField({
node,
name: 'layout',
value: layout || '',
})
// Used by createPages() to register redirects.
createNodeField({
node,
name: 'redirect',
value: redirect_from ? JSON.stringify(redirect_from) : '',
})
}
}
exports.createPages = async ({ graphql, boundActionCreators }) => {
const { createPage, createRedirect } = boundActionCreators
// Used to detect and prevent duplicate redirects
const redirectToSlugMap = {}
const allMarkdown = await graphql(
`
{
allMarkdownRemark(limit: 1000) {
edges {
node {
fields {
redirect
slug
layout
}
}
}
}
}
`
)
if (allMarkdown.errors) {
console.error (allMarkdown.errors)
throw new Error(allMarkdown.errors)
}
allMarkdown.data.allMarkdownRemark.edges.forEach(({ node }) => {
const { slug, layout, redirect } = node.fields
createPage({
path: slug,
// Feel free to set any `layout` as you'd like in the frontmatter, as
// long as the corresponding template file exists in src/templates.
// If no template is set, it will fall back to the default `page`
// template.
component: path.resolve(`./src/templates/${layout || 'page'}.tsx`),
context: {
// Data passed to context is available in page queries as GraphQL variables.
slug
}
})
// URL redirect handler
// Adapted from reactjs/reactjs.org:
// https://github.com/reactjs/reactjs.org/blob/master/gatsby-node.js#L111
if (redirect) {
let toRedirect = JSON.parse(node.fields.redirect)
if (!Array.isArray(toRedirect)) {
toRedirect = [toRedirect]
}
toRedirect.forEach(fromPath => {
if (redirectToSlugMap[fromPath] != null) {
console.error(
`Duplicate redirect detected from "${fromPath}" to:\n` +
`* ${redirectToSlugMap[fromPath]}\n` +
`* ${slug}\n`,
)
process.exit(1)
}
// A leading "/" is required for redirects to work,
// But multiple leading "/" will break redirects.
// For more context see github.com/reactjs/reactjs.org/pull/194
const toPath = slug.startsWith('/') ? slug : `/${slug}`
redirectToSlugMap[fromPath] = slug
createRedirect({
fromPath: `/${fromPath}`,
redirectInBrowser: true,
toPath
})
})
}
})
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment