-
Notifications
You must be signed in to change notification settings - Fork 10.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix(starters): Improve null checking & update to best practices #26918
Changes from all commits
f809d12
812a4ce
cb15f3e
2422f32
5247774
6d5e5c1
d2b6f38
c8afdcd
ee5cb0b
3983128
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,25 +1,26 @@ | ||
const path = require(`path`) | ||
const { createFilePath } = require(`gatsby-source-filesystem`) | ||
|
||
exports.createPages = async ({ graphql, actions }) => { | ||
exports.createPages = async ({ graphql, actions, reporter }) => { | ||
const { createPage } = actions | ||
|
||
// Define a template for blog post | ||
const blogPost = path.resolve(`./src/templates/blog-post.js`) | ||
|
||
// Get all markdown blog posts sorted by date | ||
const result = await graphql( | ||
` | ||
{ | ||
allMarkdownRemark( | ||
sort: { fields: [frontmatter___date], order: DESC } | ||
limit: 1000 | ||
) { | ||
edges { | ||
node { | ||
fields { | ||
slug | ||
} | ||
frontmatter { | ||
title | ||
} | ||
nodes { | ||
fields { | ||
slug | ||
} | ||
frontmatter { | ||
title | ||
} | ||
} | ||
} | ||
|
@@ -28,37 +29,86 @@ exports.createPages = async ({ graphql, actions }) => { | |
) | ||
|
||
if (result.errors) { | ||
throw result.errors | ||
reporter.panicOnBuild(`There was an error loading your blog posts`, result.errors) | ||
return | ||
} | ||
|
||
// Create blog posts pages. | ||
const posts = result.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 | ||
|
||
createPage({ | ||
path: post.node.fields.slug, | ||
component: blogPost, | ||
context: { | ||
slug: post.node.fields.slug, | ||
previous, | ||
next, | ||
}, | ||
const posts = result.data.allMarkdownRemark.nodes | ||
|
||
// 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 previous = index === posts.length - 1 ? null : posts[index + 1] | ||
const next = index === 0 ? null : posts[index - 1] | ||
|
||
createPage({ | ||
path: post.fields.slug, | ||
component: blogPost, | ||
context: { | ||
slug: post.fields.slug, | ||
previous, | ||
next, | ||
}, | ||
}) | ||
}) | ||
}) | ||
} | ||
} | ||
|
||
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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Haha, well that answers my question. Does it need a null check or does it have an empty There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The |
||
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 | ||
description: String | ||
date: Date @dateformat | ||
} | ||
|
||
type Fields { | ||
slug: String | ||
} | ||
`) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -35,34 +35,43 @@ const Bio = () => { | |
} | ||
`) | ||
|
||
const { author, social } = data.site.siteMetadata | ||
// Set these values by editing "siteMetadata" in gatsby-config.js | ||
const author = data.site.siteMetadata?.author | ||
const social = data.site.siteMetadata?.social | ||
|
||
const avatar = data?.avatar?.childImageSharp?.fixed | ||
|
||
return ( | ||
<div | ||
style={{ | ||
display: `flex`, | ||
marginBottom: rhythm(2.5), | ||
}} | ||
> | ||
<Image | ||
fixed={data.avatar.childImageSharp.fixed} | ||
alt={author.name} | ||
style={{ | ||
marginRight: rhythm(1 / 2), | ||
marginBottom: 0, | ||
minWidth: 50, | ||
borderRadius: `100%`, | ||
}} | ||
imgStyle={{ | ||
borderRadius: `50%`, | ||
}} | ||
/> | ||
<p> | ||
Written by <strong>{author.name}</strong> {author.summary} | ||
{` `} | ||
<a href={`https://twitter.com/${social.twitter}`}> | ||
You should follow him on Twitter | ||
</a> | ||
</p> | ||
{avatar && ( | ||
<Image | ||
fixed={avatar} | ||
alt={author?.name || ``} | ||
style={{ | ||
marginRight: rhythm(1 / 2), | ||
marginBottom: 0, | ||
minWidth: 50, | ||
borderRadius: `100%`, | ||
}} | ||
imgStyle={{ | ||
borderRadius: `50%`, | ||
}} | ||
/> | ||
)} | ||
{author?.name && ( | ||
<p> | ||
Written by <strong>{author.name}</strong> {author?.summary || null} | ||
{` `} | ||
<a href={`https://twitter.com/${social?.twitter || ``}`}> | ||
You should follow them on Twitter | ||
Comment on lines
+70
to
+71
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i think, if there is no twitter name is set, then this |
||
</a> | ||
</p> | ||
)} | ||
</div> | ||
) | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,18 +7,28 @@ import SEO from "../components/seo" | |
import { rhythm } from "../utils/typography" | ||
|
||
const BlogIndex = ({ data, location }) => { | ||
const siteTitle = data.site.siteMetadata.title | ||
const posts = data.allMarkdownRemark.edges | ||
const siteTitle = data.site.siteMetadata?.title || `Title` | ||
const posts = data.allMarkdownRemark.nodes | ||
|
||
if (posts.length === 0) { | ||
return ( | ||
<Layout location={location} title={siteTitle}> | ||
<SEO title="All posts" /> | ||
<Bio /> | ||
<p>No blog posts found. Add markdown posts to "content/blog" (or the directory you specified for the "gatsby-source-filesystem" plugin in gatsby-config.js).</p> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Big fan of this approach. Nice work. |
||
</Layout> | ||
) | ||
} | ||
|
||
return ( | ||
<Layout location={location} title={siteTitle}> | ||
<SEO title="All posts" /> | ||
<Bio /> | ||
{posts.map(({ node }) => { | ||
const title = node.frontmatter.title || node.fields.slug | ||
{posts.map((post) => { | ||
const title = post.frontmatter.title || post.fields.slug | ||
return ( | ||
<article | ||
key={node.fields.slug} | ||
key={post.fields.slug} | ||
itemScope | ||
itemType="http://schema.org/Article" | ||
> | ||
|
@@ -30,18 +40,18 @@ const BlogIndex = ({ data, location }) => { | |
> | ||
<Link | ||
style={{ boxShadow: `none` }} | ||
to={node.fields.slug} | ||
to={post.fields.slug} | ||
itemProp="url" | ||
> | ||
<span itemProp="headline">{title}</span> | ||
</Link> | ||
</h3> | ||
<small>{node.frontmatter.date}</small> | ||
<small>{post.frontmatter.date}</small> | ||
</header> | ||
<section> | ||
<p | ||
dangerouslySetInnerHTML={{ | ||
__html: node.frontmatter.description || node.excerpt, | ||
__html: post.frontmatter.description || post.excerpt, | ||
}} | ||
itemProp="description" | ||
/> | ||
|
@@ -63,17 +73,15 @@ export const pageQuery = graphql` | |
} | ||
} | ||
allMarkdownRemark(sort: { fields: [frontmatter___date], order: DESC }) { | ||
edges { | ||
node { | ||
excerpt | ||
fields { | ||
slug | ||
} | ||
frontmatter { | ||
date(formatString: "MMMM DD, YYYY") | ||
title | ||
description | ||
} | ||
nodes { | ||
excerpt | ||
fields { | ||
slug | ||
} | ||
frontmatter { | ||
date(formatString: "MMMM DD, YYYY") | ||
title | ||
description | ||
} | ||
} | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This check shouldn't be needed, because if the array is empty, the forEach never happens anyway. However, what might be needed is a null check on
allMarkdownRemark
. Does that exist if there's no markdown? Shame there's no optional chaining in gatsby-node!There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I prefer to make it more explicit as I myself can't always remember that e.g.
forEach
would behave this way