Skip to content

Commit

Permalink
feat(gatsby-remark-images-contentful): enable webp sources (#11273)
Browse files Browse the repository at this point in the history
## Description

Add a `withWebp` option to include a webp source in parsed images from contentful.

## Related Issues

Addresses #11268.


Co-authored-by: Ward Peeters <ward@coding-tech.com>
  • Loading branch information
AlbericTrancart and wardpeet committed Mar 11, 2019
1 parent 229bf43 commit 6866e76
Show file tree
Hide file tree
Showing 5 changed files with 172 additions and 84 deletions.
1 change: 1 addition & 0 deletions packages/gatsby-remark-images-contentful/README.md
Expand Up @@ -52,6 +52,7 @@ plugins: [
| `sizeByPixelDensity` | `false` | Analyze images' pixel density to make decisions about target image size. This is what GitHub is doing when embedding images in tickets. This is a useful setting for documentation pages with a lot of screenshots. It can have unintended side effects on high-pixel density artworks.<br /><br />Example: A screenshot made on a retina screen with a resolution of 144 (e.g. Macbook) and a width of 100px, will be rendered at 50px. |
| `wrapperStyle` | | Add custom styles to the div wrapping the responsive images. Use regular CSS syntax, e.g. `margin-bottom:10px; background: red;` |
| `backgroundColor` | `white` | Set the background color of the image to match the background of your design |
| `withWebp` | `false` | Additionally generate WebP versions alongside your chosen file format. They are added as a srcset with the appropriate mimetype and will be loaded in browsers that support the format. |

[1]: https://jmperezperez.com/medium-image-progressive-loading-placeholder/
[2]: https://code.facebook.com/posts/991252547593574/the-technology-behind-preview-photos/
@@ -1,38 +1,32 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`it transforms HTML img tags 1`] = `
"
<a class=\\"gatsby-resp-image-link\\" href=\\"https://images.ctfassets.net/rocybtov1ozk/wtrHxeu3zEoEce2MokCSi/73dce36715f16e27cf5ff0d2d97d7dff/quwowooybuqbl6ntboz3.jpg\\" style=\\"display: block\\" target=\\"_blank\\" rel=\\"noopener\\">
<span class=\\"gatsby-resp-image-wrapper\\" style=\\"position: relative; display: block; ; max-width: 600px; margin-left: auto; margin-right: auto;\\">
<span class=\\"gatsby-resp-image-background-image\\" style=\\"padding-bottom: 100%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image;&apos;); background-size: cover; display: block;\\">
<img class=\\"gatsby-resp-image-image\\" style=\\"width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;\\" alt=\\"quwowooybuqbl6ntboz3\\" title=\\"\\" src=\\"https://images.ctfassets.net/rocybtov1ozk/wtrHxeu3zEoEce2MokCSi/73dce36715f16e27cf5ff0d2d97d7dff/quwowooybuqbl6ntboz3.jpg\\" srcset=\\"srcSet\\" sizes=\\"128px,250px\\">
</span>
</span>
</a>
"
"<a class=\\"gatsby-resp-image-link\\" href=\\"https://images.ctfassets.net/rocybtov1ozk/wtrHxeu3zEoEce2MokCSi/73dce36715f16e27cf5ff0d2d97d7dff/quwowooybuqbl6ntboz3.jpg\\" style=\\"display: block\\" target=\\"_blank\\" rel=\\"noopener\\">
<span class=\\"gatsby-resp-image-wrapper\\" style=\\"position: relative; display: block; ; max-width: 600px; margin-left: auto; margin-right: auto;\\">
<span class=\\"gatsby-resp-image-background-image\\" style=\\"padding-bottom: 100%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image;&apos;); background-size: cover; display: block;\\">
<img class=\\"gatsby-resp-image-image\\" style=\\"width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;\\" alt=\\"quwowooybuqbl6ntboz3\\" title=\\"\\" src=\\"https://images.ctfassets.net/rocybtov1ozk/wtrHxeu3zEoEce2MokCSi/73dce36715f16e27cf5ff0d2d97d7dff/quwowooybuqbl6ntboz3.jpg\\" srcset=\\"srcSet\\" sizes=\\"128px,250px\\">
</span>
</span>
</a>"
`;
exports[`it transforms images in markdown 1`] = `
"
<a
class=\\"gatsby-resp-image-link\\"
href=\\"https://images.ctfassets.net/rocybtov1ozk/wtrHxeu3zEoEce2MokCSi/73dce36715f16e27cf5ff0d2d97d7dff/quwowooybuqbl6ntboz3.jpg\\"
style=\\"display: block\\"
target=\\"_blank\\"
rel=\\"noopener\\"
>
<span
class=\\"gatsby-resp-image-wrapper\\"
style=\\"position: relative; display: block; ; max-width: 600px; margin-left: auto; margin-right: auto;\\"
>
<span
class=\\"gatsby-resp-image-background-image\\"
style=\\"padding-bottom: 100%; position: relative; bottom: 0; left: 0; background-image: url('data:image;'); background-size: cover; display: block;\\"
>
<img
"<a
class=\\"gatsby-resp-image-link\\"
href=\\"https://images.ctfassets.net/rocybtov1ozk/wtrHxeu3zEoEce2MokCSi/73dce36715f16e27cf5ff0d2d97d7dff/quwowooybuqbl6ntboz3.jpg\\"
style=\\"display: block\\"
target=\\"_blank\\"
rel=\\"noopener\\"
>
<span
class=\\"gatsby-resp-image-wrapper\\"
style=\\"position: relative; display: block; ; max-width: 600px; margin-left: auto; margin-right: auto;\\"
>
<span
class=\\"gatsby-resp-image-background-image\\"
style=\\"padding-bottom: 100%; position: relative; bottom: 0; left: 0; background-image: url('data:image;'); background-size: cover; display: block;\\"
>
<img
class=\\"gatsby-resp-image-image\\"
style=\\"width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;\\"
alt=\\"image\\"
Expand All @@ -41,32 +35,67 @@ exports[`it transforms images in markdown 1`] = `
srcset=\\"srcSet\\"
sizes=\\"128px,250px\\"
/>
</span>
</span>
</a>
"
</span>
</span>
</a>"
`;

exports[`it transforms images with a https scheme in markdown 1`] = `
"
<a
class=\\"gatsby-resp-image-link\\"
href=\\"https://images.ctfassets.net/rocybtov1ozk/wtrHxeu3zEoEce2MokCSi/73dce36715f16e27cf5ff0d2d97d7dff/quwowooybuqbl6ntboz3.jpg\\"
style=\\"display: block\\"
target=\\"_blank\\"
rel=\\"noopener\\"
>
exports[`it transforms images in markdown with webp srcSets if option is enabled 1`] = `
"<a
class=\\"gatsby-resp-image-link\\"
href=\\"https://images.ctfassets.net/rocybtov1ozk/wtrHxeu3zEoEce2MokCSi/73dce36715f16e27cf5ff0d2d97d7dff/quwowooybuqbl6ntboz3.jpg\\"
style=\\"display: block\\"
target=\\"_blank\\"
rel=\\"noopener\\"
>
<span
class=\\"gatsby-resp-image-wrapper\\"
style=\\"position: relative; display: block; ; max-width: 600px; margin-left: auto; margin-right: auto;\\"
>
<span
class=\\"gatsby-resp-image-background-image\\"
style=\\"padding-bottom: 100%; position: relative; bottom: 0; left: 0; background-image: url('data:image;'); background-size: cover; display: block;\\"
>
<picture>
<source
srcset=\\"webpSrcSet\\"
sizes=\\"128px,250px\\"
type=\\"image/webp\\"
/>
<source
srcset=\\"srcSet\\"
sizes=\\"128px,250px\\"
/>
<img
class=\\"gatsby-resp-image-image\\"
style=\\"width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;\\"
alt=\\"image\\"
title=\\"\\"
src=\\"https://images.ctfassets.net/rocybtov1ozk/wtrHxeu3zEoEce2MokCSi/73dce36715f16e27cf5ff0d2d97d7dff/quwowooybuqbl6ntboz3.jpg\\"
/>
</picture>
</span>
</span>
</a>"
`;

<span
class=\\"gatsby-resp-image-wrapper\\"
style=\\"position: relative; display: block; ; max-width: 600px; margin-left: auto; margin-right: auto;\\"
>
<span
class=\\"gatsby-resp-image-background-image\\"
style=\\"padding-bottom: 100%; position: relative; bottom: 0; left: 0; background-image: url('data:image;'); background-size: cover; display: block;\\"
>
<img
exports[`it transforms images with a https scheme in markdown 1`] = `
"<a
class=\\"gatsby-resp-image-link\\"
href=\\"https://images.ctfassets.net/rocybtov1ozk/wtrHxeu3zEoEce2MokCSi/73dce36715f16e27cf5ff0d2d97d7dff/quwowooybuqbl6ntboz3.jpg\\"
style=\\"display: block\\"
target=\\"_blank\\"
rel=\\"noopener\\"
>
<span
class=\\"gatsby-resp-image-wrapper\\"
style=\\"position: relative; display: block; ; max-width: 600px; margin-left: auto; margin-right: auto;\\"
>
<span
class=\\"gatsby-resp-image-background-image\\"
style=\\"padding-bottom: 100%; position: relative; bottom: 0; left: 0; background-image: url('data:image;'); background-size: cover; display: block;\\"
>
<img
class=\\"gatsby-resp-image-image\\"
style=\\"width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;\\"
alt=\\"image\\"
Expand All @@ -75,9 +104,7 @@ exports[`it transforms images with a https scheme in markdown 1`] = `
srcset=\\"srcSet\\"
sizes=\\"128px,250px\\"
/>
</span>
</span>
</a>
"
</span>
</span>
</a>"
`;
19 changes: 19 additions & 0 deletions packages/gatsby-remark-images-contentful/src/__tests__/index.js
Expand Up @@ -13,6 +13,7 @@ jest.mock(`../utils/`, () => {
base64: `data:image;`,
aspectRatio: 1,
srcSet: `srcSet`,
webpSrcSet: `webpSrcSet`,
src: `imageUrl`,
sizes: [`128px`, `250px`],
density: 140,
Expand Down Expand Up @@ -194,3 +195,21 @@ test(`it leaves relative HTML img tags alone`, async () => {
const nodes = await plugin(createPluginOptions(content, imagePath))
expect(nodes[0].value).toBe(content)
})

test(`it transforms images in markdown with webp srcSets if option is enabled`, async () => {
const imagePath = `//images.ctfassets.net/rocybtov1ozk/wtrHxeu3zEoEce2MokCSi/73dce36715f16e27cf5ff0d2d97d7dff/quwowooybuqbl6ntboz3.jpg`
const content = `
![image](${imagePath})
`.trim()

const nodes = await plugin(createPluginOptions(content, imagePath), {
withWebp: true,
})

expect(nodes.length).toBe(1)

const node = nodes.pop()
expect(node.type).toBe(`html`)
expect(node.value).toMatchSnapshot()
expect(node.value).not.toMatch(`<html>`)
})
87 changes: 60 additions & 27 deletions packages/gatsby-remark-images-contentful/src/index.js
Expand Up @@ -25,6 +25,7 @@ module.exports = async (
linkImagesToOriginal: true,
showCaptions: false,
pathPrefix,
withWebp: false,
}

// This will only work for markdown syntax image tags
Expand Down Expand Up @@ -91,20 +92,8 @@ module.exports = async (
const fileNameNoExt = fileName.replace(/\.[^/.]+$/, ``)
const defaultAlt = fileNameNoExt.replace(/[^A-Z0-9]/gi, ` `)

// Construct new image node w/ aspect ratio placeholder
let rawHTML = `
<span
class="gatsby-resp-image-wrapper"
style="position: relative; display: block; ${
options.wrapperStyle
}; max-width: ${presentationWidth}px; margin-left: auto; margin-right: auto;"
>
<span
class="gatsby-resp-image-background-image"
style="padding-bottom: ${ratio}; position: relative; bottom: 0; left: 0; background-image: url('${
responsiveSizesResult.base64
}'); background-size: cover; display: block;"
>
// Create our base image tag
let imageTag = `
<img
class="gatsby-resp-image-image"
style="width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px ${
Expand All @@ -116,22 +105,66 @@ module.exports = async (
srcset="${srcSet}"
sizes="${responsiveSizesResult.sizes}"
/>
</span>
</span>
`
`.trim()

// if options.withWebp is enabled, generate a webp version and change the image tag to a picture tag
if (options.withWebp) {
imageTag = `
<picture>
<source
srcset="${responsiveSizesResult.webpSrcSet}"
sizes="${responsiveSizesResult.sizes}"
type="image/webp"
/>
<source
srcset="${srcSet}"
sizes="${responsiveSizesResult.sizes}"
/>
<img
class="gatsby-resp-image-image"
style="width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px ${
options.backgroundColor
};"
alt="${node.alt ? node.alt : defaultAlt}"
title="${node.title ? node.title : ``}"
src="${fallbackSrc}"
/>
</picture>
`.trim()
}

// Construct new image node w/ aspect ratio placeholder
let rawHTML = `
<span
class="gatsby-resp-image-wrapper"
style="position: relative; display: block; ${
options.wrapperStyle
}; max-width: ${presentationWidth}px; margin-left: auto; margin-right: auto;"
>
<span
class="gatsby-resp-image-background-image"
style="padding-bottom: ${ratio}; position: relative; bottom: 0; left: 0; background-image: url('${
responsiveSizesResult.base64
}'); background-size: cover; display: block;"
>
${imageTag}
</span>
</span>
`.trim()

// Make linking to original image optional.
if (options.linkImagesToOriginal) {
rawHTML = `
<a
class="gatsby-resp-image-link"
href="${originalImg}"
style="display: block"
target="_blank"
rel="noopener"
>
${rawHTML}
</a>
`
<a
class="gatsby-resp-image-link"
href="${originalImg}"
style="display: block"
target="_blank"
rel="noopener"
>
${rawHTML}
</a>
`.trim()
}

// Wrap in figure and use title as caption
Expand Down
8 changes: 8 additions & 0 deletions packages/gatsby-remark-images-contentful/src/utils/index.js
Expand Up @@ -51,10 +51,18 @@ const buildResponsiveSizes = async ({ metadata, imageUrl, options = {} }) => {
.map(size => `${imageUrl}?w=${Math.round(size)} ${Math.round(size)}w`)
.join(`,\n`)

const webpSrcSet = filteredSizes
.map(
size => `${imageUrl}?fm=webp&w=${Math.round(size)} ${Math.round(size)}w`
)
.join(`,\n`)

// TODO think about a better structure to save srcset types instead of adding them to the root
return {
base64: base64Img,
aspectRatio,
srcSet,
webpSrcSet,
src: imageUrl,
sizes: options.sizes,
density,
Expand Down

0 comments on commit 6866e76

Please sign in to comment.