diff --git a/gatsby-node.js b/gatsby-node.js index 6f0b7f25792..c4bac87ad52 100644 --- a/gatsby-node.js +++ b/gatsby-node.js @@ -1,266 +1,31 @@ -/* eslint-env node */ - -const fs = require('fs') -const path = require('path') -const GithubSlugger = require('github-slugger') -const { createFilePath } = require('gatsby-source-filesystem') -const tagToSlug = require('./src/utils/shared/tagToSlug') -const pagesGenerator = require('./src/utils/node/paginatable') -const { siteMetadata } = require('./gatsby-config') - -const { getItemBySource } = require('./src/utils/shared/sidebar') - -const remark = require('remark') -const remarkHTML = require('remark-html') - -const markdownToHtml = remark().use(remarkHTML).processSync -const slugger = new GithubSlugger() - -// Generate hedings data from markdown - -const SLUG_REGEXP = /\s+{#([a-z0-9-]*[a-z0-9]+)}\s*$/ - -function extractSlugFromTitle(title) { - // extracts expressions like {#too-many-files} from the end of a title - const meta = title.match(SLUG_REGEXP) - - if (meta) { - return [title.substring(0, meta.index), meta[1]] - } - return [title, slugger.slug(title)] -} - -const parseHeadings = text => { - const headingRegex = /\n(## \s*)(.*)/g - const matches = [] - let match - do { - match = headingRegex.exec(text) - if (match) { - const [title, slug] = extractSlugFromTitle(match[2]) - matches.push({ - text: title, - slug: slug - }) - } - } while (match) - - slugger.reset() - return matches -} +const { + getNodeSlug, + setPageContext, + removePageTrailingSlash +} = require('./src/utils/node/common') +const { createPages: createDocPages } = require('./src/utils/node/doc') +const { createPages: createBlogPages } = require('./src/utils/node/blog') exports.onCreateNode = ({ node, actions, getNode }) => { const { createNodeField } = actions if (node.internal.type === 'MarkdownRemark') { - const contentPath = path.join(__dirname, 'content') - const source = node.fileAbsolutePath.replace(contentPath, '') - let value - - if (source.startsWith('/blog')) { - value = createFilePath({ - getNode, - node, - trailingSlash: false - }).replace(/^\/blog\/[0-9\-]*/, '/blog/') - - // Convert fields in frontmatter from markdown to html - const { - frontmatter: { descriptionLong, pictureComment } - } = node - - if (descriptionLong) { - node.frontmatter.descriptionLong = markdownToHtml( - descriptionLong - ).contents - } - - if (pictureComment) { - node.frontmatter.pictureComment = markdownToHtml( - pictureComment - ).contents - } - // end Convert fields - } else { - value = getItemBySource(source).path - } - createNodeField({ name: 'slug', node, - value + value: getNodeSlug(node, getNode) }) } } exports.createPages = async ({ graphql, actions }) => { - // DOCS - const docsResponse = await graphql( - ` - { - docs: allMarkdownRemark( - filter: { fileAbsolutePath: { regex: "/content/docs/" } } - limit: 9999 - ) { - edges { - node { - rawMarkdownBody - fields { - slug - } - } - } - } - } - ` - ) - - if (docsResponse.errors) { - throw docsResponse.errors - } - - const docComponent = path.resolve('./src/templates/doc-home.tsx') - - docsResponse.data.docs.edges.forEach(doc => { - const headings = parseHeadings(doc.node.rawMarkdownBody) - - if (doc.node.fields.slug) { - actions.createPage({ - component: docComponent, - path: doc.node.fields.slug, - context: { - isDocs: true, - slug: doc.node.fields.slug, - headings - } - }) - } - }) - - // Blog - const blogResponse = await graphql( - ` - { - allMarkdownRemark( - sort: { fields: [frontmatter___date], order: DESC } - filter: { fileAbsolutePath: { regex: "/content/blog/" } } - limit: 9999 - ) { - edges { - node { - fields { - slug - } - frontmatter { - title - } - } - } - } - home: allMarkdownRemark( - sort: { fields: [frontmatter___date], order: DESC } - filter: { fileAbsolutePath: { regex: "/content/blog/" } } - limit: 9999 - ) { - pageInfo { - itemCount - } - } - tags: allMarkdownRemark(limit: 9999) { - group(field: frontmatter___tags) { - fieldValue - pageInfo { - itemCount - } - } - } - } - ` - ) - - if (blogResponse.errors) { - throw blogResponse.errors - } - - // Create home blog pages (with pagination) - const blogHomeTemplate = path.resolve('./src/templates/blog-home.tsx') - - for (const page of pagesGenerator({ - basePath: '/blog', - hasHeroItem: true, - itemCount: blogResponse.data.home.pageInfo.itemCount - })) { - actions.createPage({ - component: blogHomeTemplate, - path: page.path, - context: { - isBlog: true, - ...page.context - } - }) - } - - // Create blog posts pages - const blogPostTemplate = path.resolve('./src/templates/blog-post.tsx') - const posts = blogResponse.data.allMarkdownRemark.edges - - posts.forEach((post, index) => { - const previous = index === posts.length - 1 ? null : posts[index + 1].node - const next = index === 0 ? null : posts[index - 1].node - - actions.createPage({ - component: blogPostTemplate, - context: { - isBlog: true, - currentPage: index + 1, - next, - previous, - slug: post.node.fields.slug - }, - path: post.node.fields.slug - }) - }) - - // Create tags pages (with pagination) - const blogTagsTemplate = path.resolve('./src/templates/blog-tags.tsx') - - blogResponse.data.tags.group.forEach( - ({ fieldValue: tag, pageInfo: { itemCount } }) => { - const basePath = `/tags/${tagToSlug(tag)}` - - for (const page of pagesGenerator({ basePath, itemCount })) { - actions.createPage({ - component: blogTagsTemplate, - path: page.path, - context: { tag, ...page.context } - }) - } - } - ) + createDocPages({ graphql, actions }) + createBlogPages({ graphql, actions }) } -const is404Regexp = /^\/404/ -const trailingSlashRegexp = /\/$/ - exports.onCreatePage = ({ page, actions }) => { - // Set necessary flags for pageContext - const newPage = { - ...page, - context: { - ...page.context, - is404: is404Regexp.test(page.path) - } - } - - // Remove trailing slash - if (page.path !== '/' && trailingSlashRegexp.test(newPage.path)) { - newPage.path = newPage.path.replace(trailingSlashRegexp, '') - } - - if (newPage !== page) { - actions.deletePage(page) - actions.createPage(newPage) - } + setPageContext(page, actions) + removePageTrailingSlash(page, actions) } // Ignore warnings about CSS inclusion order, because we use CSS modules. diff --git a/src/utils/node/blog.js b/src/utils/node/blog.js new file mode 100644 index 00000000000..21a740aeb35 --- /dev/null +++ b/src/utils/node/blog.js @@ -0,0 +1,151 @@ +const path = require('path') +const tagToSlug = require('../shared/tagToSlug') +const { BLOG } = require('../../consts') + +const pageUrl = (basePath, page) => { + if (page > 1) { + const basePrefix = basePath === '/' ? '' : `${basePath}/` + + return `${basePrefix}page/${page}` + } + + return basePath +} + +function* pagesGenerator({ itemCount, hasHeroItem = false, basePath }) { + let currentPage = 1 + let skip = 0 + + while (skip < itemCount) { + const limit = + hasHeroItem && currentPage === 1 + ? BLOG.postsPerPage - BLOG.postsPerRow + 1 + : BLOG.postsPerPage + + let nextPage + let previousPage + + if (skip + limit < itemCount) { + nextPage = pageUrl(basePath, currentPage + 1) + } + + if (skip > 0) { + previousPage = pageUrl(basePath, currentPage - 1) + } + + // For the Paginator component + const pageInfo = { currentPage, nextPage, previousPage } + + yield { + path: pageUrl(basePath, currentPage), + context: { limit, pageInfo, skip } + } + + currentPage++ + skip += limit + } +} + +const createPages = async ({ graphql, actions }) => { + const blogResponse = await graphql( + ` + { + allMarkdownRemark( + sort: { fields: [frontmatter___date], order: DESC } + filter: { fileAbsolutePath: { regex: "/content/blog/" } } + limit: 9999 + ) { + edges { + node { + fields { + slug + } + frontmatter { + title + } + } + } + } + home: allMarkdownRemark( + sort: { fields: [frontmatter___date], order: DESC } + filter: { fileAbsolutePath: { regex: "/content/blog/" } } + limit: 9999 + ) { + pageInfo { + itemCount + } + } + tags: allMarkdownRemark(limit: 9999) { + group(field: frontmatter___tags) { + fieldValue + pageInfo { + itemCount + } + } + } + } + ` + ) + + if (blogResponse.errors) { + throw blogResponse.errors + } + + // Create home blog pages (with pagination) + const blogHomeTemplate = path.resolve('./src/templates/blog-home.tsx') + + for (const page of pagesGenerator({ + basePath: '/blog', + hasHeroItem: true, + itemCount: blogResponse.data.home.pageInfo.itemCount + })) { + actions.createPage({ + component: blogHomeTemplate, + path: page.path, + context: { + isBlog: true, + ...page.context + } + }) + } + + // Create blog posts pages + const blogPostTemplate = path.resolve('./src/templates/blog-post.tsx') + const posts = blogResponse.data.allMarkdownRemark.edges + + posts.forEach((post, index) => { + const previous = index === posts.length - 1 ? null : posts[index + 1].node + const next = index === 0 ? null : posts[index - 1].node + + actions.createPage({ + component: blogPostTemplate, + context: { + isBlog: true, + currentPage: index + 1, + next, + previous, + slug: post.node.fields.slug + }, + path: post.node.fields.slug + }) + }) + + // Create tags pages (with pagination) + const blogTagsTemplate = path.resolve('./src/templates/blog-tags.tsx') + + blogResponse.data.tags.group.forEach( + ({ fieldValue: tag, pageInfo: { itemCount } }) => { + const basePath = `/tags/${tagToSlug(tag)}` + + for (const page of pagesGenerator({ basePath, itemCount })) { + actions.createPage({ + component: blogTagsTemplate, + path: page.path, + context: { tag, ...page.context } + }) + } + } + ) +} + +exports.createPages = createPages diff --git a/src/utils/node/common.js b/src/utils/node/common.js new file mode 100644 index 00000000000..2319b1593b5 --- /dev/null +++ b/src/utils/node/common.js @@ -0,0 +1,69 @@ +const path = require('path') +const process = require('process') +const { createFilePath } = require('gatsby-source-filesystem') +const remark = require('remark') +const remarkHTML = require('remark-html') +const { getItemBySource } = require('../shared/sidebar') + +const markdownToHtml = remark().use(remarkHTML).processSync +const is404Regexp = /^\/404/ +const trailingSlashRegexp = /\/$/ + +const getNodeSlug = (node, getNode) => { + const contentPath = path.join(process.cwd(), 'content') + const source = node.fileAbsolutePath.replace(contentPath, '') + let value + + if (source.startsWith('/blog')) { + value = createFilePath({ + getNode, + node, + trailingSlash: false + }).replace(/^\/blog\/[0-9\-]*/, '/blog/') + + // Convert fields in frontmatter from markdown to html + const { + frontmatter: { descriptionLong, pictureComment } + } = node + + if (descriptionLong) { + node.frontmatter.descriptionLong = markdownToHtml( + descriptionLong + ).contents + } + + if (pictureComment) { + node.frontmatter.pictureComment = markdownToHtml(pictureComment).contents + } + // end Convert fields + } else { + value = getItemBySource(source).path + } + + return value +} + +const setPageContext = (page, actions) => { + actions.deletePage(page) + actions.createPage({ + ...page, + context: { + ...page.context, + is404: is404Regexp.test(page.path) + } + }) +} + +const removePageTrailingSlash = (page, actions) => { + if (page.path !== '/' && trailingSlashRegexp.test(page.path)) { + actions.deletePage(page) + actions.createPage({ + ...page, + path: page.path.replace(trailingSlashRegexp, '') + }) + } +} + +exports.getNodeSlug = getNodeSlug +exports.setPageContext = setPageContext +exports.removePageTrailingSlash = removePageTrailingSlash diff --git a/src/utils/node/doc.js b/src/utils/node/doc.js new file mode 100644 index 00000000000..d8ee79d64a4 --- /dev/null +++ b/src/utils/node/doc.js @@ -0,0 +1,81 @@ +const path = require('path') +const GithubSlugger = require('github-slugger') + +const slugger = new GithubSlugger() +const SLUG_REGEXP = /\s+{#([a-z0-9-]*[a-z0-9]+)}\s*$/ + +const extractSlugFromTitle = title => { + // extracts expressions like {#too-many-files} from the end of a title + const meta = title.match(SLUG_REGEXP) + + if (meta) { + return [title.substring(0, meta.index), meta[1]] + } + return [title, slugger.slug(title)] +} + +const parseHeadings = text => { + const headingRegex = /\n(## \s*)(.*)/g + const matches = [] + let match + do { + match = headingRegex.exec(text) + if (match) { + const [title, slug] = extractSlugFromTitle(match[2]) + matches.push({ + text: title, + slug: slug + }) + } + } while (match) + + slugger.reset() + return matches +} + +const createPages = async ({ graphql, actions }) => { + // DOCS + const docsResponse = await graphql( + ` + { + docs: allMarkdownRemark( + filter: { fileAbsolutePath: { regex: "/content/docs/" } } + limit: 9999 + ) { + edges { + node { + rawMarkdownBody + fields { + slug + } + } + } + } + } + ` + ) + + if (docsResponse.errors) { + throw docsResponse.errors + } + + const docComponent = path.resolve('./src/templates/doc-home.tsx') + + docsResponse.data.docs.edges.forEach(doc => { + const headings = parseHeadings(doc.node.rawMarkdownBody) + + if (doc.node.fields.slug) { + actions.createPage({ + component: docComponent, + path: doc.node.fields.slug, + context: { + isDocs: true, + slug: doc.node.fields.slug, + headings + } + }) + } + }) +} + +exports.createPages = createPages diff --git a/src/utils/node/paginatable.js b/src/utils/node/paginatable.js deleted file mode 100644 index e745c3b5a6c..00000000000 --- a/src/utils/node/paginatable.js +++ /dev/null @@ -1,50 +0,0 @@ -/* eslint-env node */ -const { BLOG } = require('../../consts') - -function pageUrl(basePath, page) { - if (page > 1) { - const basePrefix = basePath === '/' ? '' : `${basePath}/` - - return `${basePrefix}page/${page}` - } - - return basePath -} - -module.exports = function* pagesGenerator({ - itemCount, - hasHeroItem = false, - basePath -}) { - let currentPage = 1 - let skip = 0 - - while (skip < itemCount) { - const limit = - hasHeroItem && currentPage === 1 - ? BLOG.postsPerPage - BLOG.postsPerRow + 1 - : BLOG.postsPerPage - - let nextPage - let previousPage - - if (skip + limit < itemCount) { - nextPage = pageUrl(basePath, currentPage + 1) - } - - if (skip > 0) { - previousPage = pageUrl(basePath, currentPage - 1) - } - - // For the Paginator component - const pageInfo = { currentPage, nextPage, previousPage } - - yield { - path: pageUrl(basePath, currentPage), - context: { limit, pageInfo, skip } - } - - currentPage++ - skip += limit - } -}