256 lines
6.3 KiB
JavaScript
256 lines
6.3 KiB
JavaScript
|
const path = require(`path`)
|
||
|
const _ = require('lodash')
|
||
|
const { createFilePath } = require(`gatsby-source-filesystem`)
|
||
|
|
||
|
const recentPosts = []
|
||
|
const archives = {}
|
||
|
const tagsFrequent = []
|
||
|
|
||
|
exports.createPages = async ({ graphql, actions, reporter }) => {
|
||
|
const { createPage } = actions
|
||
|
|
||
|
// Define a template for blog post
|
||
|
const blogPostTemplate = path.resolve(`./src/templates/blog-post.tsx`)
|
||
|
const blogListTemplate = path.resolve(`./src/templates/blog-list.tsx`)
|
||
|
const blogSearchTemplate = path.resolve(`./src/templates/blog-search.tsx`)
|
||
|
|
||
|
// Get all markdown blog posts sorted by date
|
||
|
const result = await graphql(
|
||
|
`
|
||
|
{
|
||
|
allMarkdownRemark(
|
||
|
sort: { fields: [frontmatter___date], order: DESC }
|
||
|
limit: 1000
|
||
|
filter: { fileAbsolutePath: { regex: "/content/blog/" } }
|
||
|
) {
|
||
|
nodes {
|
||
|
id
|
||
|
fields {
|
||
|
slug
|
||
|
}
|
||
|
frontmatter {
|
||
|
title
|
||
|
date(formatString: "YYYY")
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
tagsGroup: allMarkdownRemark(limit: 1000) {
|
||
|
group(field: frontmatter___tags) {
|
||
|
name: fieldValue
|
||
|
totalCount
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
`
|
||
|
)
|
||
|
|
||
|
if (result.errors) {
|
||
|
reporter.panicOnBuild(
|
||
|
`There was an error loading your blog posts`,
|
||
|
result.errors
|
||
|
)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
const posts = result.data.allMarkdownRemark.nodes
|
||
|
recentPosts.push(
|
||
|
...posts.slice(0, 10).map((p) => ({
|
||
|
slug: p.fields.slug,
|
||
|
title: p.frontmatter.title
|
||
|
}))
|
||
|
)
|
||
|
|
||
|
const tags = result.data.tagsGroup.group
|
||
|
tagsFrequent.push(
|
||
|
...tags.sort((a, b) => b.totalCount - a.totalCount).slice(0, 10)
|
||
|
)
|
||
|
|
||
|
// side bar data for each page
|
||
|
posts.forEach((d) => {
|
||
|
if (archives[d.frontmatter.date] == null) archives[d.frontmatter.date] = 0
|
||
|
archives[d.frontmatter.date]++
|
||
|
})
|
||
|
|
||
|
// Create blog posts pages
|
||
|
// But only if there's at least one markdown file found at "content/blog" (defined in gatsby-config.js)
|
||
|
// `context` is available in the template as a prop and as a variable in GraphQL
|
||
|
|
||
|
if (posts.length > 0) {
|
||
|
posts.forEach((post, index) => {
|
||
|
const previousPostId = index === 0 ? null : posts[index - 1].id
|
||
|
const nextPostId = index === posts.length - 1 ? null : posts[index + 1].id
|
||
|
|
||
|
createPage({
|
||
|
path: post.fields.slug,
|
||
|
component: blogPostTemplate,
|
||
|
context: {
|
||
|
id: post.id,
|
||
|
archives,
|
||
|
recentPosts,
|
||
|
tags: tagsFrequent,
|
||
|
previousPostId,
|
||
|
nextPostId
|
||
|
}
|
||
|
})
|
||
|
})
|
||
|
}
|
||
|
|
||
|
// Create blog-list pages
|
||
|
const postsPerPage = 6
|
||
|
const numPages = Math.ceil(posts.length / postsPerPage)
|
||
|
Array.from({ length: numPages }).forEach((_, i) => {
|
||
|
createPage({
|
||
|
path: i === 0 ? `/blog` : `/blog/page/${i + 1}`,
|
||
|
component: blogListTemplate,
|
||
|
context: {
|
||
|
page: 'index',
|
||
|
archives,
|
||
|
recentPosts,
|
||
|
tags: tagsFrequent,
|
||
|
filter: { fileAbsolutePath: { regex: '/content/blog/' } },
|
||
|
limit: postsPerPage,
|
||
|
skip: i * postsPerPage,
|
||
|
numPages: numPages,
|
||
|
currentPage: i + 1
|
||
|
}
|
||
|
})
|
||
|
})
|
||
|
|
||
|
for (year in archives) {
|
||
|
const count = archives[year]
|
||
|
const numPagesOfYear = Math.ceil(count / postsPerPage)
|
||
|
Array.from({ length: numPagesOfYear }).forEach((_, i) => {
|
||
|
createPage({
|
||
|
path: i === 0 ? `/${year}/` : `/${year}/page/${i + 1}`,
|
||
|
component: blogListTemplate,
|
||
|
context: {
|
||
|
page: 'year',
|
||
|
archives,
|
||
|
recentPosts,
|
||
|
tags: tagsFrequent,
|
||
|
filter: {
|
||
|
frontmatter: { date: { gte: year, lt: year + 1 } },
|
||
|
fileAbsolutePath: { regex: '/content/blog/' }
|
||
|
},
|
||
|
limit: postsPerPage,
|
||
|
skip: i * postsPerPage,
|
||
|
numPages: numPagesOfYear,
|
||
|
currentPage: i + 1,
|
||
|
year: year
|
||
|
}
|
||
|
})
|
||
|
})
|
||
|
}
|
||
|
|
||
|
tags.forEach((tag) => {
|
||
|
const count = tag.totalCount
|
||
|
const numPagesOfTag = Math.ceil(count / postsPerPage)
|
||
|
Array.from({ length: numPagesOfTag }).forEach((__, i) => {
|
||
|
const tagPath = `/category/${_.kebabCase(tag.name)}/`
|
||
|
createPage({
|
||
|
path: i === 0 ? tagPath : `${tagPath}page/${i + 1}`,
|
||
|
component: blogListTemplate,
|
||
|
context: {
|
||
|
page: 'category',
|
||
|
archives,
|
||
|
recentPosts,
|
||
|
tags: tagsFrequent,
|
||
|
filter: { frontmatter: { tags: { in: [tag.name] } } },
|
||
|
limit: postsPerPage,
|
||
|
skip: i * postsPerPage,
|
||
|
numPages: numPagesOfTag,
|
||
|
currentPage: i + 1,
|
||
|
tag: tag.name
|
||
|
}
|
||
|
})
|
||
|
})
|
||
|
})
|
||
|
|
||
|
createPage({
|
||
|
path: '/search/',
|
||
|
component: blogSearchTemplate,
|
||
|
context: {
|
||
|
page: 'search',
|
||
|
archives,
|
||
|
recentPosts,
|
||
|
tags: tagsFrequent
|
||
|
}
|
||
|
})
|
||
|
}
|
||
|
|
||
|
exports.onCreatePage = ({ page, actions }) => {
|
||
|
const { createPage, deletePage } = actions
|
||
|
|
||
|
if (page.path == '/404/') {
|
||
|
deletePage(page)
|
||
|
createPage({
|
||
|
...page,
|
||
|
context: {
|
||
|
...page.context,
|
||
|
archives,
|
||
|
recentPosts,
|
||
|
tags: tagsFrequent
|
||
|
}
|
||
|
})
|
||
|
}
|
||
|
}
|
||
|
|
||
|
exports.onCreateNode = ({ node, actions, getNode }) => {
|
||
|
const { createNodeField } = actions
|
||
|
|
||
|
if (node.internal.type === `MarkdownRemark`) {
|
||
|
const value = createFilePath({ node, getNode })
|
||
|
|
||
|
createNodeField({
|
||
|
name: `slug`,
|
||
|
node,
|
||
|
value
|
||
|
})
|
||
|
}
|
||
|
}
|
||
|
|
||
|
exports.createSchemaCustomization = ({ actions }) => {
|
||
|
const { createTypes } = actions
|
||
|
|
||
|
// Explicitly define the siteMetadata {} object
|
||
|
// This way those will always be defined even if removed from gatsby-config.js
|
||
|
|
||
|
// Also explicitly define the Markdown frontmatter
|
||
|
// This way the "MarkdownRemark" queries will return `null` even when no
|
||
|
// blog posts are stored inside "content/blog" instead of returning an error
|
||
|
createTypes(`
|
||
|
type SiteSiteMetadata {
|
||
|
author: Author
|
||
|
siteUrl: String
|
||
|
social: Social
|
||
|
}
|
||
|
|
||
|
type Author {
|
||
|
name: String
|
||
|
summary: String
|
||
|
}
|
||
|
|
||
|
type Social {
|
||
|
twitter: String
|
||
|
}
|
||
|
|
||
|
type MarkdownRemark implements Node {
|
||
|
frontmatter: Frontmatter
|
||
|
fields: Fields
|
||
|
}
|
||
|
|
||
|
type Frontmatter {
|
||
|
title: String
|
||
|
date: Date @dateformat
|
||
|
author: String
|
||
|
authorLink: String
|
||
|
previewImg: File @fileByRelativePath
|
||
|
tags: [String!]!
|
||
|
}
|
||
|
|
||
|
type Fields {
|
||
|
slug: String
|
||
|
}
|
||
|
`)
|
||
|
}
|