Sam Willis
Sam Willis

Reputation: 4211

Gatsby markdown frontmatter link becomes a relative file path after GraphQL static query

I am trying to store content for a button that will navigate the user to a different page in my markdown frontmatter, as it is to be editable through Netlify CMS.

When the GraphQL static query brings back data, my frontmatter has been converted into a relative file path rather than the intended page URL. This doesn't happen with page queries.

footer.md:

---
templateKey: footer
title: Some footer title
logo: /img/logo.svg
links:
  - url: /prices/
    text: Prices
  - url: /privacy-policy/
    text: Privacy policy
---

GraphQL static query:

const queryData = useStaticQuery(graphql`
    query FooterComponent {
      site {
        siteMetadata {
          title
        }
      }
      markdownRemark(frontmatter: { templateKey: { eq: "footer" } }) {
        frontmatter {
          title
          logo {
            publicURL
          }
          links {
            url
            text
          }
        }
      }
    }
  `);

Data that gets returned:

{
"title":"Some footer title",
"logo":{"publicURL":"/static/6731239d1e0c5fcdf0f825a8de82be10/logo.svg"},
"links":[
    {"url":"../pages/prices","text":"Prices"},
    {"url":"../pages/privacy-policy","text":"Privacy policy"}
  ]
}

You can see that the URL for the objects in the links array have been converted to relative file paths, rather than returning the actual link from the frontmatter as it is in the original markdown file.

How can I retrieve the actual link?

Upvotes: 0

Views: 592

Answers (1)

Derek Nguyen
Derek Nguyen

Reputation: 11577

Reading your comment on the other answer, I think the issue is with fmImagesToRelative. It keeps track of all created nodes, then tries to match each frontmatter field value with all file node's path.

A quickfix would to 'preserve' those frontmatter fields before running fmImagesToRelative, then restore them afterward. So you would literally work around that function, like this:

exports.onCreateNode = ({ node }) => {
  const isRemarkNode = node.internal.type === 'MarkdownRemark'
  const originalFields = {}
  if (isRemarkNode) {
    Object.entries(node.frontmatter).forEach(([k, v]) => {
      if (['links', 'some_other_field'].includes(k)) {
        /* preserve */
        originalFields[k] = v
        /* remove the field to avoid unnecessary work */
        delete node.frontmatter[k]
      }
    })
  }

  /* this function won't work without access to literally all nodes, hence why we leave it in the middle like this */
  fmImagesToRelative(node)

  if (isRemarkNode) {
    /* restore */
    node.frontmatter = {
      ...node.frontmatter,
      ...originalFields
    }
  }
}

Personally, I prefer to only modify fields that I know are files. With your example, I would rather declare that 'logo' is an image file, than excluding 'links'.

As a fellow NetlifyCMS user, I use createSchemaCustomization to modify known file fields... It's rather complicated (as with anything Gatsby), so I wrote a plugin to simplify this.

So my approach would look like this:


exports.createSchemaCustomization = ({ actions }) => {
  actions.createTypes(`
    type Frontmatter @infer {
      logo: File @fileByAbsolutePath(path: "static")
    }

    type MarkdownRemark implements Node @infer {
      frontmatter: Frontmatter
    }
  `)
}

In the example above, @fileByAbsolutePath is a field extension that takes logo value (i.e img/logo.png) & resolve it with static, so when you query your markdown file you'd get a File node pointing to root/static/img/logo.png.


It would be neat for fmImagesToRelative to have exclude/include arguments, though I'm not a fan of the blanket approach.

Upvotes: 3

Related Questions