diff --git a/gatsby-config.js b/gatsby-config.js index 4a7ba3ea89..50825507e4 100644 --- a/gatsby-config.js +++ b/gatsby-config.js @@ -7,6 +7,7 @@ require('./config/prismjs/usage') const apiMiddleware = require('./src/server/middleware/api') const redirectsMiddleware = require('./src/server/middleware/redirects') +const makeFeedHtml = require('./plugins/utils/makeFeedHtml') const { BLOG } = require('./src/consts') const title = 'Data Version Control ยท DVC' @@ -41,11 +42,11 @@ const plugins = [ } }, { + resolve: 'gatsby-source-filesystem', options: { name: 'images', path: path.join(__dirname, 'static', 'uploads') - }, - resolve: 'gatsby-source-filesystem' + } }, { resolve: 'gatsby-transformer-remark', @@ -54,10 +55,10 @@ const plugins = [ 'gatsby-remark-embedder', 'gatsby-remark-dvc-linker', { + resolve: 'gatsby-remark-prismjs', options: { noInlineHighlight: true - }, - resolve: 'gatsby-remark-prismjs' + } }, { resolve: 'gatsby-remark-smartypants', @@ -73,9 +74,7 @@ const plugins = [ }, 'gatsby-remark-relative-images', 'gatsby-remark-copy-linked-files', - { - resolve: 'gatsby-remark-external-links' - }, + 'gatsby-remark-external-links', { resolve: 'gatsby-remark-autolink-headers', options: { @@ -120,33 +119,35 @@ const plugins = [ } }, { + resolve: `gatsby-plugin-feed`, options: { feeds: [ { description, output: '/blog/rss.xml', query: ` - { - allBlogPost( - sort: { fields: [date], order: DESC } - ) { - nodes { - html - slug - title - date - description - } + { + allBlogPost( + sort: { fields: [date], order: DESC } + ) { + nodes { + htmlAst + slug + title + date + description } } - `, + } + `, serialize: ({ query: { site, allBlogPost } }) => { return allBlogPost.nodes.map(node => { + const html = makeFeedHtml(node.htmlAst, site.siteMetadata.siteUrl) return Object.assign( {}, { /* eslint-disable-next-line @typescript-eslint/camelcase */ - custom_elements: [{ 'content:encoded': node.html }], + custom_elements: [{ 'content:encoded': html }], title: node.title, date: node.date, description: node.description, @@ -170,9 +171,8 @@ const plugins = [ } } } - ` - }, - resolve: `gatsby-plugin-feed` + ` + } }, { resolve: 'gatsby-plugin-sentry', @@ -201,11 +201,11 @@ const plugins = [ if (process.env.CONTEXT === 'production') { plugins.push({ + resolve: 'gatsby-plugin-google-analytics', options: { respectDNT: true, trackingId: process.env.GA_ID - }, - resolve: 'gatsby-plugin-google-analytics' + } }) } diff --git a/plugins/utils/makeFeedHtml.js b/plugins/utils/makeFeedHtml.js new file mode 100644 index 0000000000..40fc6def98 --- /dev/null +++ b/plugins/utils/makeFeedHtml.js @@ -0,0 +1,53 @@ +const { imageWrapperClass } = require('gatsby-remark-images/constants') +const unified = require('unified') +const { convertHastToHtml } = require('./convertHast.js') +const { select, selectAll } = require('hast-util-select') + +const rootToAbsolute = siteUrl => tree => { + selectAll('a', tree).forEach(node => { + if (node.properties.href.startsWith('/')) { + node.properties.href = siteUrl + node.properties.href + } + }) + selectAll('img', tree).forEach(node => { + if (node.properties.src.startsWith('/')) { + node.properties.src = siteUrl + node.properties.src + } + }) + return tree +} + +const unwrapImages = () => tree => { + selectAll(`.${imageWrapperClass}`, tree).forEach(node => { + // Set the fallback image as the wrapper's only child, and then + // give that image the wrapper's original style. + const fallbackImg = select('img', node) + node.children = [ + { + ...fallbackImg, + properties: { + ...fallbackImg.properties, + style: 'max-width: 100%; margin: auto;' + } + } + ] + }) + return tree +} + +function makeFeedHtml(htmlAst, siteUrl) { + // We add the rootToAbsolute processor before usage because it depends on siteUrl. + return convertHastToHtml( + unified() + /* + All images processed by Gatsby to be responsive are "unwrapped" into + their fallback 'img' nodes, as RSS doesn't work with the tricks that + true HTML does. + */ + .use(unwrapImages) + .use(rootToAbsolute, siteUrl) + .runSync(htmlAst) + ) +} + +module.exports = makeFeedHtml