diff --git a/packages/gatsby-remark-images-contentful/README.md b/packages/gatsby-remark-images-contentful/README.md index 19c11b0e71669..d2a2185f54506 100644 --- a/packages/gatsby-remark-images-contentful/README.md +++ b/packages/gatsby-remark-images-contentful/README.md @@ -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.

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/ diff --git a/packages/gatsby-remark-images-contentful/src/__tests__/__snapshots__/index.js.snap b/packages/gatsby-remark-images-contentful/src/__tests__/__snapshots__/index.js.snap index 8ce88e806f5ce..f0ed1de3bf9e5 100644 --- a/packages/gatsby-remark-images-contentful/src/__tests__/__snapshots__/index.js.snap +++ b/packages/gatsby-remark-images-contentful/src/__tests__/__snapshots__/index.js.snap @@ -1,38 +1,32 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`it transforms HTML img tags 1`] = ` -" - - - - - \\"quwowooybuqbl6ntboz3\\" - - - - - " +" + + + \\"quwowooybuqbl6ntboz3\\" + + + " `; exports[`it transforms images in markdown 1`] = ` -" - - - - - + + + \\"image\\" - - - - - " + + + " `; -exports[`it transforms images with a https scheme in markdown 1`] = ` -" - +exports[`it transforms images in markdown with webp srcSets if option is enabled 1`] = ` +" + + + + + + \\"image\\" + + + + " +`; - - - + + + \\"image\\" - - - - - " + + + " `; diff --git a/packages/gatsby-remark-images-contentful/src/__tests__/index.js b/packages/gatsby-remark-images-contentful/src/__tests__/index.js index f8b5fbf1c77a9..87376ce5a3750 100644 --- a/packages/gatsby-remark-images-contentful/src/__tests__/index.js +++ b/packages/gatsby-remark-images-contentful/src/__tests__/index.js @@ -13,6 +13,7 @@ jest.mock(`../utils/`, () => { base64: `data:image;`, aspectRatio: 1, srcSet: `srcSet`, + webpSrcSet: `webpSrcSet`, src: `imageUrl`, sizes: [`128px`, `250px`], density: 140, @@ -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(``) +}) diff --git a/packages/gatsby-remark-images-contentful/src/index.js b/packages/gatsby-remark-images-contentful/src/index.js index 3eb9374c73cb8..46d5c2e47c457 100644 --- a/packages/gatsby-remark-images-contentful/src/index.js +++ b/packages/gatsby-remark-images-contentful/src/index.js @@ -25,6 +25,7 @@ module.exports = async ( linkImagesToOriginal: true, showCaptions: false, pathPrefix, + withWebp: false, } // This will only work for markdown syntax image tags @@ -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 = ` - - + // Create our base image tag + let imageTag = ` - - - ` + `.trim() + + // if options.withWebp is enabled, generate a webp version and change the image tag to a picture tag + if (options.withWebp) { + imageTag = ` + + + + ${node.alt ? node.alt : defaultAlt} + + `.trim() + } + + // Construct new image node w/ aspect ratio placeholder + let rawHTML = ` + + + ${imageTag} + + + `.trim() + // Make linking to original image optional. if (options.linkImagesToOriginal) { rawHTML = ` - -${rawHTML} - - ` + + ${rawHTML} + + `.trim() } // Wrap in figure and use title as caption diff --git a/packages/gatsby-remark-images-contentful/src/utils/index.js b/packages/gatsby-remark-images-contentful/src/utils/index.js index d189679ad9d2f..b3ba9833ac9e9 100644 --- a/packages/gatsby-remark-images-contentful/src/utils/index.js +++ b/packages/gatsby-remark-images-contentful/src/utils/index.js @@ -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,