Gatsby Develop: cannot destructure property `frontmatter` of undefined - javascript

I'm new to Gatsby and GraphQL. I was trying to build a blog-like website. I'm using a template for the blog post page which leads to the use of gatsby-node.js and I think this is causing the issue. It runs fine in development mode but as I try to build it, it gives me an error. I'm on the latest version of everything.
Command: Gatsby build
Error:
Also, here are my files attached.
gatsby-node.js
exports.createPages = async ({ actions, graphql, reporter, ...props }) => {
console.log(actions, graphql, reporter, props)
const { createPage } = actions
const blogPostTemplate = path.resolve(`src/pages/lead-generation.js`)
const result = await graphql(`
{
allMarkdownRemark(limit: 1000) {
edges {
node {
frontmatter {
path
}
}
}
}
}
`)
// Handle errors
if (result.errors) {
reporter.panicOnBuild(`Error while running GraphQL query.`)
return
}
result.data.allMarkdownRemark.edges.forEach(({ node }) => {
createPage({
path: node.frontmatter.path,
component: blogPostTemplate,
context: {}, // additional data can be passed via context
})
})
}
lead-generation.js
export default class Template extends React.Component {
render() {
const { markdownRemark: { frontmatter = {}, html } = {} } =
this.props.data;
return (
<div className="p-3">
<div>
<h1>{frontmatter.title}</h1>
<h6 className="pb-5">{frontmatter.articleHeading}</h6>
<div
className="blog-post-content"
dangerouslySetInnerHTML={{ __html: html }}
/>
</div>
<div
id="container"
className="p-5"
style={{ height: 500, width: 400 }}
>
{/* widget*/}
</div>
</div>
)
}
}
export const pageQuery = graphql`
query($path: String!) {
markdownRemark(frontmatter: { path: { eq: $path } }) {
html
frontmatter {
title
articleHeading
categoryID
}
}
}
`
markdown-file.md
---
path: "/works/remaining-URL"
title: "some title?"
articleHeading: "This is some heading"
categoryID: 10056
---
This is the further text
I have simplified the question out. Please let me know if you need further elaboration.

Related

Dynamic table of contents won't deploy on Vercel (... but works on localhost)

I'm trying to add a dynamic table of contents to my blogpage in next.js.
The code is running perfectly on my localhost, but as soon as I'm deploying it to vercel I got this error:
TypeError: Cannot read properties of undefined (reading 'content')
at BlogPost (/vercel/path0/.next/server/pages/posts/[slug].js:111:23)
at Jc (/vercel/path0/node_modules/react-dom/cjs/react-dom-server.browser.production.min.js:64:191)
at Mc (/vercel/path0/node_modules/react-dom/cjs/react-dom-server.browser.production.min.js:66:253)
at Z (/vercel/path0/node_modules/react-dom/cjs/react-dom-server.browser.production.min.js:71:89)
at Nc (/vercel/path0/node_modules/react-dom/cjs/react-dom-server.browser.production.min.js:73:98)
at Mc (/vercel/path0/node_modules/react-dom/cjs/react-dom-server.browser.production.min.js:67:131)
at Z (/vercel/path0/node_modules/react-dom/cjs/react-dom-server.browser.production.min.js:71:89)
at Mc (/vercel/path0/node_modules/react-dom/cjs/react-dom-server.browser.production.min.js:70:13)
at Z (/vercel/path0/node_modules/react-dom/cjs/react-dom-server.browser.production.min.js:71:89)
at Nc (/vercel/path0/node_modules/react-dom/cjs/react-dom-server.browser.production.min.js:73:98)
I found out that the build failure is produced by the command .processSync on line 85 (I wrote a comment there). Sadly I'm unable to fix this...
Any suggestions and help why this happens?
Here is the full code:
( I delete the grahpcms route when creating the GraphQLClient for safety, so that's not the failure here.)
import { GraphQLClient, gql } from "graphql-request";
import { useRouter } from "next/router";
import { unified } from "unified";
import rehypeParse from "rehype-parse/lib";
import rehypeStringify from "rehype-stringify/lib";
import { visit } from "unist-util-visit";
import parameterize from "parameterize";
const graphcms = new GraphQLClient();
const QUERY = gql`
query Post($slug: String!) {
post(where: { slug: $slug }) {
title
id
content {
html
}
datePublish
coverPhoto {
url
}
datePublish
}
}
`;
const SLUGLIST = gql`
{
posts {
slug
}
}
`;
export async function getStaticPaths() {
const { posts } = await graphcms.request(SLUGLIST);
return {
paths: posts.map((post) => ({ params: { slug: post.slug } })),
fallback: true,
};
}
export async function getStaticProps({ params }) {
const slug = params.slug;
const data = await graphcms.request(QUERY, { slug });
const post = data.post;
return {
props: {
post,
},
};
}
export default function BlogPost({ post }) {
const router = useRouter();
var toc = [];
//Forms the HTML String into a tree that we can add logic too
//Then forms that tree back into html string
const newContent = unified()
.use(rehypeParse, {
fragment: true,
})
.use(() => {
return (tree) => {
visit(tree, "element", (node) => {
if (node.tagName === "h2") {
const id = parameterize(node.children[0].value);
node.properties.id = id;
toc.push({
id: node.properties.id,
title: node.children[0].value,
});
console.log("id", id);
}
});
};
})
.use(rehypeStringify)
//THIS IS WHERE THE DELPLOYMENT FAILS
.processSync(post.content.html)
.toString();
if (router.isFallback) {
return <h2>Loading</h2>;
}
return (
<div>
<header>
<h1>{post.title}</h1>
<img
src={post.coverPhoto.url}
width="100%"
style={{ borderRadius: "1rem" }}></img>
<span>Published: {post.datePublish}</span>
</header>
<main>
<div>
{toc.map(({ id, title }) => {
return (
<li style={{ listStyle: "none" }} key={id}>
<a style={{ fontSize: "1.1rem" }} href={`#${id}`}>
<b> {title}</b>
</a>
</li>
);
})}
</div>
<div
className="blogpost"
dangerouslySetInnerHTML={{ __html: newContent }}
/>
</main>
</div>
);
}
Thank you very much!
I would try to use operator of optional changing like this:
post?.content?.html
Second step is to build project on your computer not to wait for building on vercel and detect another error.
P. S. You can handle undefined props by if statement but only don't forget to place it before main return statement and after all hooks.

Gatsby MDX showing title and slug but doesn't find page

I'm learning Gatsby and I wanted to use MDX for blog pages. I followed the tutorial here to programmatically create pages.
I can see them in my GraphQL, and displaying the list of all the articles is working with their title and slug, but when I click on the link to open them or type their address I have a 404 page. Do you have an idea from where it can come from?
This is my code:
gatsby-node.js:
exports.onCreateNode = ({ node, actions, getNode }) => {
const { createNodeField } = actions
if (node.internal.type === "Mdx") {
const value = createFilePath({ node, getNode })
createNodeField({
name: "slug",
node,
value: `/blog${value}`,
})
}
}
gatsby-config.js:
plugins: [
{
resolve: `gatsby-plugin-mdx`,
options: {
defaultLayouts: { default: path.resolve('./src/layouts/post.js') },
extensions: [`.mdx`, `.md`],
},
},
{
resolve: `gatsby-source-filesystem`,
options: {
name: `posts`,
path: `${__dirname}/src/posts`
}
},
],
index.js (with the list):
<ul>
{posts.map(({ node: post }) => (
<li key={post.id}>
<Link to={post.fields.slug}>
<h2>{post.frontmatter.title}</h2>
</Link>
<p>{post.excerpt}</p>
</li>
))}
</ul>
Query in index.js
export const pageQuery = graphql`
query blogIndex {
allMdx {
edges {
node {
id
excerpt
frontmatter {
title
}
fields {
slug
}
}
}
}
}
`
The template:
<div>
<div className="content" dangerouslySetInnerHTML={{ __html: post.html }}></div>
</div>
And the query for the template:
export const pageQuery = graphql`
query BlogPostQuery($id: String) {
mdx(id: { eq: $id }) {
id
body
frontmatter {
title
}
}
}
`
I made a repo if you want to test it: https://github.com/JulSeb42/gatsby-mdx
Thanks for your answers!
You are missing the page creation part. In your gatsby-node.js add the following:
const path = require("path")
exports.createPages = async ({ graphql, actions, reporter }) => {
// Destructure the createPage function from the actions object
const { createPage } = actions
const result = await graphql(`
query {
allMdx {
edges {
node {
id
fields {
slug
}
}
}
}
}
`)
if (result.errors) {
reporter.panicOnBuild('🚨 ERROR: Loading "createPages" query')
}
// Create blog post pages.
const posts = result.data.allMdx.edges
// you'll call `createPage` for each result
posts.forEach(({ node }, index) => {
createPage({
// This is the slug you created before
// (or `node.frontmatter.slug`)
path: node.fields.slug,
// This component will wrap our MDX content
component: path.resolve(`./src/components/posts-page-layout.js`),
// You can use the values in this context in
// our page layout component
context: { id: node.id },
})
})
}
Regarding your other issue, it should be a separate question but, if a component is exported as default, you don't need the curly braces when importing it. In addition, you need to return a value.

Building a Wordpress blog archive: filtering by date with GatsbyJS

Currently struggling with building an archive for a Wordpress blog using Gatsby. I'm trying to set it up where you navigate to a url like "localhost:8000/blog/11-19" and it shows all of the blog posts for November 2019. Right now I DO have the site set up where the url is created successfully based off the date, but when I try to loop through the query and actually generate the blog content, nothing appears. My gatsby-node file:
const path = require('path')
module.exports.createPages = async ({ graphql, actions }) => {
const { createPage } = actions
const blogPostTemplate = path.resolve('./src/templates/blog-post.js')
const blogCategoryFilter = path.resolve('./src/templates/blog-filter-category.js')
const blogArchiveFilter = path.resolve('./src/templates/blog-filter-archive.js')
const res = await graphql(`
query {
allWordpressPost {
edges {
node {
slug
date(formatString:"MM-YY")
}
}
}
allWordpressCategory {
edges {
node {
slug
}
}
}
}
`)
res.data.allWordpressPost.edges.forEach((edge) => {
createPage({
component: blogPostTemplate,
path: `/blog/${edge.node.slug}`,
context: {
slug: edge.node.slug,
}
})
})
res.data.allWordpressPost.edges.forEach((edge) => {
createPage({
component: blogArchiveFilter,
path: `/blog/${edge.node.date}`,
context: {
slug: edge.node.date,
}
})
})
res.data.allWordpressCategory.edges.forEach((edge) => {
createPage({
component: blogCategoryFilter,
path: `/blog/category/${edge.node.slug}`,
context: {
slug: edge.node.slug,
}
})
})
}
My template file (in this example, that's blogArchiveFilter--blog-filter-archive.js):
import React from 'react'
import { graphql, Link } from 'gatsby'
import Layout from '../components/layout'
import BlogNav from '../components/blognav'
import blogStyles from '../components/modules/blog.module.css'
export const query = graphql`
query($slug: Date!) {
allWordpressPost (filter: { date: { eq: $slug }}) {
edges {
node {
title
slug
content
date(formatString: "MMMM DD, YYYY")
}
}
}
}
`
export default ({ data }) => {
return (
<Layout>
<div className={blogStyles.blog_container}>
<div className={blogStyles.blogContent_container}>
<ol>
{data.allWordpressPost.edges.map((edge) => {
return (
<div className={blogStyles.blogPost_container}>
<li className={blogStyles.blog_list}>
<h2><Link to={`/blog/${edge.node.slug}`} className={blogStyles.blog_title} dangerouslySetInnerHTML={{ __html: edge.node.title }}></Link></h2>
<p className={blogStyles.blog_date}>{edge.node.date}</p>
<p className={blogStyles.blog_content} dangerouslySetInnerHTML={{ __html: edge.node.content }} />
</li>
</div>
)
})}
</ol>
</div>
<BlogNav />
</div>
</Layout>
)
}
EDIT:
Is there a way that I can set a variable within a query to change in response to a slug? For example: if the URL was "localhost:8000/blog/2019-11", I could filter gt: "$slug" and filter lt "2019-12". If the URL was "localhost:8000/blog/2019-10" I could filter gt: "$slug" and filter lt "2019-11". This would always generate the posts for the month and year in my URL. Example is below.
It's like if the slug is "2019-(a)", then the lt value would always be "2019-(a+1)". Can that be done?
query($slug: Date!){
allWordpressPost (filter: { date: { gt: $slug, lt: "2019-12" }}) {
edges {
node {
date(formatString: "MMMM DD, YYYY")
}
}
}
}

Filtering Wordpress categories in Gatsby

I'm currently creating a website using Gatsby and querying data from Wordpress using the gatsby-source-wordpress plug-in. In my gatsby-node file I've set things up to dynamically create web pages based on a post's category. When I try to run a query sorting by category slug and filtering by post date, I receive an error: "Error: The result of this StaticQuery could not be fetched."
Can someone take a look at this and let me know where I'm going wrong exactly?
The gatsby-node file:
const path = require('path')
module.exports.createPages = async ({ graphql, actions }) => {
const { createPage } = actions
const blogPostTemplate = path.resolve('./src/templates/blog-post.js')
const blogCategoryFilter = path.resolve('./src/templates/blog-filter-category.js')
const res = await graphql(`
query {
allWordpressPost {
edges {
node {
slug
}
}
}
allWordpressCategory {
edges {
node {
slug
}
}
}
}
`)
res.data.allWordpressPost.edges.forEach((edge) => {
createPage({
component: blogPostTemplate,
path: `/blog/${edge.node.slug}`,
context: {
slug: edge.node.slug,
}
})
})
res.data.allWordpressCategory.edges.forEach((edge) => {
createPage({
component: blogCategoryFilter,
path: `/blog/${edge.node.slug}`,
context: {
slug: edge.node.slug,
}
})
})
}
The template file I'm using to actually generate the filtered and sorted content (blog-filter-category.js):
import React from "react"
import Layout from '../components/layout'
import { Link, graphql, useStaticQuery } from 'gatsby'
import BlogNav from '../components/blognav'
import blogStyles from '../components/modules/blog.module.css'
const BlogPage = () => {
const data = useStaticQuery(graphql`
query($slug: String!) {
allWordpressCategory (filter: { slug: { eq: $slug } }) {
edges {
node {
name
}
}
}
allWordpressPost (sort: {fields:date, order:DESC}) {
edges {
node {
title
slug
content
date(formatString: "MMMM DD, YYYY")
}
}
}
}
`)
return (
<Layout>
<div className={blogStyles.blog_container}>
<div className={blogStyles.blogContent_container}>
<ol>
{data.allWordpressPost.edges.map((edge) => {
return (
<div className={blogStyles.blogPost_container}>
<li className={blogStyles.blog_list}>
<h2><Link to={`/blog/${edge.node.slug}`} className={blogStyles.blog_title} dangerouslySetInnerHTML={{ __html: edge.node.title }}></Link></h2>
<p className={blogStyles.blog_date}>{edge.node.date}</p>
<p className={blogStyles.blog_content} dangerouslySetInnerHTML={{ __html: edge.node.content }} />
</li>
</div>
)
})}
</ol>
</div>
<BlogNav />
</div>
</Layout>
)
}
export default BlogPage
I also tried querying this instead in my blog-filter-category.js file:
query($slug: String!) {
allWordpressPost (filter: {categories: {elemMatch: {slug: { eq: $slug }}}}) {
edges {
node {
title
slug
content
date(formatString: "MMMM DD, YYYY")
}
}
}
}
It seemed closer but I ended up netting the same error message. I'm lost! Thanks in advance for your help.
Congrats on you choice to build with Gatsby.
First problem I see is that you try to use variables in you static queries. This is not possible at the moment. Variable passed to page context are only available in page queries.
Page queries are defined in page templates as simple exports like:
import { graphql } from 'gatsby'
export const query = graphql`
query MyQuery($slug: String!) {
allWordpressPost (filter: {categories: {elemMatch: {slug: { eq: $slug }}}}) {
edges {
node {
title
slug
content
date(formatString: "MMMM DD, YYYY")
}
}
}
}`
Learn more about differences between page and static queries in Gatsby here:
https://www.gatsbyjs.org/docs/static-query/#how-staticquery-differs-from-page-query

Gatsby: How to solve tslint expects component to receive props when it comes from a query into the component itself

I use Gatsby with Typescript to create a blog based on Contentful CMS.
I have FeaturedPost component which I want to put in the main page and this is the code:
FeaturedPost.tsx
interface IProps {
data: {
contentfulPost: ContentfulPost;
};
}
const FeaturedPost: React.FunctionComponent<IProps> = ({ data }) => {
const { title, description } = data.contentfulPost;
return (
<>
<Header>{title}</Header>;
<div>{description}</div>
</>
);
};
export const query = graphql`
query featuredPost($slug: String) {
contentfulPost(slug: { eq: $slug }) {
title
slug
description {
childMarkdownRemark {
html
}
}
}
}
`;
export default FeaturedPost;
This is my index page code:
index.tsx
const IndexPage: React.FunctionComponent<P> = () => {
return (
<Layout>
<SEO
title="Home"
keywords={[`gatsby`, `application`, `react`]}
description="Index for something I can't remember?!"
/>
<FeaturedPost />
<h1>Hi people</h1>
<p>Welcome to your new Gatsby site.</p>
<p>Now go build something great.</p>
<div style={{ maxWidth: `300px`, marginBottom: `1.45rem` }} />
</Layout>
);
};
export default IndexPage;
Tslint now expects that I pass a prop called data to FeaturedPost component since I implement interface IProps on FeaturedPost, but actually there is no data to get passed.
FeaturedPost itself receives it as a response from the sent query. Do you have any idea what's wrong with this code or how can I satisfy the linter?
In Gatsby v2, graphql queries in non-page components will be ignored. Use StaticQuery instead. Here's a small example:
import * as React from 'react'
import { StaticQuery, graphql, Link } from 'gatsby'
type TOCDataType = {
readonly allSitePage: {
edges: [
{
node: {
id: string
path: string
}
}
]
}
}
const TableOfContents: React.FunctionComponent<{}> = () => (
<StaticQuery
query={graphql`
query SitePageQuery {
allSitePage {
edges {
node {
id
path
}
}
}
}
`}
render={(data: TOCDataType) => (
<div>
{data.allSitePage.edges.map(({ node }) => (
<li key={node.id}>
<Link to={node.path}>{node.path}</Link>
</li>
))}
</div>
)}
/>
)
export default TableOfContents

Categories

Resources