Skip to content

Commit

Permalink
Fix images in the RSS feed (#1245)
Browse files Browse the repository at this point in the history
* Make root-relative links absolute in feed.

This runs the feed's htmlAst (from the remark transformer) through a processor
that changes all root-relative links (/example) into absolute
links (https://dvc.org/example), which is what many non-browser consumers of
HTML expect.

* Finish getting things working with both processors.

This commit combines the root-relative rewriting with now-working image
unwrapping to deliver working images to the RSS feed!

The processor definition is also moved into the feed scope to take advantage of
the feed query to get the root URL.

* Move the feed plugin back to its original space.

Since moving it out of gatsby-config won't be as simple as I thought, I decided
to leave it out of the scope of this branch.

* Extract feed processor from gatsby-config to its own file

Also changed a bit around implementation:

1. Replaced `rehype-urls`, which depended on older rehype libs, with a local
   implementation of the same thing for root-to-absolute linking.

2. The new feed util leans on the existing rehype-to-html function, and now the
   new processor `.use`s less `unified` plugins as it only does AST processing.

* Change unwrapped image styling in a way that respects original max-width

* Revert package.json to its pre-branch form.

* Use remark image wrapper class from its exported constants

* Clean formatting of blog feed query

* Change plugin definitions to all have "resolve" first.
  • Loading branch information
rogermparent authored May 6, 2020
1 parent b8cd773 commit 2ea63aa
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 25 deletions.
50 changes: 25 additions & 25 deletions gatsby-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -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',
Expand All @@ -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',
Expand All @@ -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: {
Expand Down Expand Up @@ -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,
Expand All @@ -170,9 +171,8 @@ const plugins = [
}
}
}
`
},
resolve: `gatsby-plugin-feed`
`
}
},
{
resolve: 'gatsby-plugin-sentry',
Expand Down Expand Up @@ -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'
}
})
}

Expand Down
53 changes: 53 additions & 0 deletions plugins/utils/makeFeedHtml.js
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit 2ea63aa

Please sign in to comment.