Gatsby getting image path after graphql query has

2019-07-30 08:47发布

问题:

So I've written a blog site in Gatsby and Remark. I've structured my posts like this:

Library/
 -- category-name/
 ---- article-name/
 ------ index.md

This has worked really well and results in me being able to make paths like /category-name/article-name.

What I also would like to do is to be able to drop an image in there called 'hero.jpg' and for it to be automatically picked up by Gatsby without having to add a frontmatter reference to it.

I've managed to get so far by adding the following to 'gatsby-node.js':

const hero = (fs.existsSync(path.resolve(__dirname, `src/library/pages/${slug}hero.jpg`))) ? `${slug}hero.jpg` : ''
createNodeField({
      node,
      name: 'hero',
      value: hero,
    })

This works as far as the graphql data goes and I now see the following:

{
  "node": {
  "id": "3190922a-2207-5621-a7be-e02be9e8da24",
  "fields": {
    "hero": "/category-name/article-name/hero.jpg"
  },
},

However on the actual page, the image link /category-name/article-name/hero.jpg doesn't exist so I get a dead image. I know this is because my image path is being transformed by gatsby-transformer-sharp but I don't know how to work out what it is being transformed to.

I believe I need to do something akin to the answers on this SO question but that seems to expect you to know that the relativePath is at the time you are writing your query but I won't have that information until after the query has returned the first time.

OnCreateNode hook added for claarity


exports.onCreateNode = ({ node, getNode, actions }) => {
  const { createNodeField } = actions
  // Add slug to MarkdownRemark node
  if (node.internal.type === 'MarkdownRemark') {
    const slug = createFilePath({ node, getNode, basePath: 'library' })
    const hero = (fs.existsSync(path.resolve(__dirname, `src/library/pages/${slug}hero.jpg`))) ? './hero.jpg' : ''

    createNodeField({
      node,
      name: 'slug',
      value: slug,
    })
    createNodeField({
      node,
      name: 'hero',
      value: hero,
    })
  }
}

回答1:

I realized my previous answer was incorrect & overly complicated (It relies on node creation order, also there's no need to add fields to imageSharp nodes. Here's the link if someone's interested.). Here's the better answer:

Querying image of hero name in the same folder with mardown

Since the hero image is always at the same directory as the markdown file, we can simply query it based on its directory.

            dir              name  ext
┌────────────┴────────────┐ ┌─┴─┐ ┌─┴─┐
absolute/path/to/directory/ hero  .png
absolute/path/to/directory/ index .md

Graphql query:

file ( dir: { eq: "absolute/path/to/directory" }, name: { "hero" } ) {
  childImageSharp {
    fixed {
      src
    }
  }
}

The only modification you need to make to your gatsby-node.js is to add this dir field to your page's context so we can use it as a variable. We can get this dir by doing path.parse(node.fileAbsolutePath).dir, or get the dir field from remark's parent node getNode(node.parent.id).dir

   +  const { dir } = getNode(node.parent.id)

      createPage({
        path: node.fields.slug,
        component,
        context: {
   +      dir,
          slug: node.fields.slug,
        },
      })

And query it like so:

    export const pageQuery = graphql`
   -  query ArticleByPath($slug: String!) {
   +  query ArticleByPath($slug: String!, $dir: String!) {
        markdownRemark(fields: { slug: { eq: $slug } }) {
          id
          htmlAst
          frontmatter {
            title
          }
        }
   +    file (dir: { eq: $dir }, name: { eq: "hero" }) {
   +      childImageSharp {
   +        fixed {
   +          src
   +        }
   +      }
   +    }
      }
    `

And use it:

    export default function Template({ data }) {
      const post = data.markdownRemark
  +   const hero = data.file ? data.file.childImageSharp : null
      return (
        <div className="landing-page-container">
          <Helmet title={`Your Blog Name - ${post.frontmatter.title}`} />
          <div className="blog-post">
  +         {hero && <img src={hero.fixed.src} alt={post.frontmatter.title} />}
            <h1>{post.frontmatter.title}</h1>
            <div className="blog-post-content">{renderAst(post.htmlAst)}</div>
          </div>
        </div>
      )
    }

Here's the gist for article.js and gatsby-node.js.