I am using Gatsby with Netlify CMS. I use gatsby-transformer-sharp for various image manipulations.
In Netlify CMS, if a user deletes an image, the frontmatter value becomes an empty string, e.g:
my-blog-post.md:
---
image: ''
---
This causes gatsby-transformer-sharp to error, when I am querying Graphql:
Error Field "image" must not have a selection since type "String" has no subfields.
This seems to be because Gatsby/Graphql has inferred the image field to be a string.
So I created a schema.md
file, so there will always be at least one entry with a valid image in place:
_schema.md:
---
image: /some-dummy-image.jpg
---
Which seems to partially fix the problem - the build only fails occasionally. But it does still fail. I think it must infer its schema from the first markdown file it comes across - sometimes it finds _schema.md
first, sometimes it finds my-blog-post.md
first.
Has anyone managed to find a solution to this?
I managed to solve this in the end. I didn't realise that it was actually easy to directly remove these empty fields from frontmatter, before the schema is inferred.
I made a little custom plugin that would recursively go through frontmatter fields, and any fields that had an empty string are deleted. I also only allowed it to delete fields with a specific name (e.g: image
) so it wouldnt cause unexpected changes elsewhere.
src/plugins/remove-empty-fields/gatsby-node.js:
let fieldsToRemove = [];
const deleteFieldsRecursive = (node) => {
fieldsToRemove.forEach(fieldToRemove => {
if (node[fieldToRemove] === '') {
delete node[fieldToRemove];
}
});
if (typeof node === 'object') {
Object.values(node).forEach(subNode => {
deleteFieldsRecursive(subNode);
})
}
};
exports.onCreateNode = ({ node }, configOptions) => {
fieldsToRemove = configOptions.fieldsToRemove;
if (node.internal.type === 'MarkdownRemark') {
if (!node.frontmatter) {
return;
}
deleteFieldsRecursive(node);
}
};
Then added this to the plugins list in gatsby-config.js
{
resolve: 'remove-empty-fields',
options: {
fieldsToRemove: [
'image',
'bgImage',
],
},
},
Update
Since Gatsby@^2.2.0, the best way to handle this error is to use the Gatsby hook createSchemaCustomization
. We need to tell Gatsby that the image
field will definitely be a file.
exports.createSchemaCustomization = ({ actions }) => {
actions.createType(`
type RemarkFrontmatter @infer {
image: File
}
type MarkdownRemark implements Node @infer {
frontmatter: RemarkFrontmatter
}
`)
}
Old answer
I've recently learned that you can modify a markdown's frontmatter directly before its MarkdownRemark
node is even created, and come back here to share.
gatsby-transformer-remark
use graymatter
to parse frontmatter & allows users to pass in a custom parser. Internally, graymatter use js-yaml
.
const yaml = require('js-yaml');
const customParser = (str) => {
const result = yaml.safeLoad(str);
// remove image (non recursively, as an example)
const { image, ...withoutImage } = result;
return withoutImage;
}
And pass it into gatsby-transformer-remark
{
resolve: `gatsby-transformer-remark`,
options: {
engines: {
yaml: customParser,
},
}
Syntactically, I prefer the way you do it -- it's clearer when the empty fields are handled by a plugin. Just wanna add another option!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With