From 9db13b3dbd69b95b34d8ed5070b73a5baf30463a Mon Sep 17 00:00:00 2001 From: polarathene Date: Mon, 15 Jun 2020 14:58:02 +1200 Subject: [PATCH 1/9] chore: Refactor `base64Width`, include in plugin defaults This feature was added when an internal fixed value of `20` was in use. It was configurable via graphql query parameters as well as overriding the default value in the plugin config, but this was not obvious as no default was set. As the default is `20`, it has been relocated to the plugin defaults, and the redundant method to get the `base64Width` value has been removed(this was implemented before the plugin defaults and options were refactored out to a separate file). --- packages/gatsby-plugin-sharp/src/index.js | 8 +++----- packages/gatsby-plugin-sharp/src/plugin-options.js | 3 ++- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/packages/gatsby-plugin-sharp/src/index.js b/packages/gatsby-plugin-sharp/src/index.js index 0e89278068769..a3864dde64565 100644 --- a/packages/gatsby-plugin-sharp/src/index.js +++ b/packages/gatsby-plugin-sharp/src/index.js @@ -283,12 +283,10 @@ function batchQueueImageResizing({ file, transforms = [], reporter }) { }) } -// A value in pixels(Int) -const defaultBase64Width = () => getPluginOptions().base64Width || 20 async function generateBase64({ file, args = {}, reporter }) { const pluginOptions = getPluginOptions() const options = healOptions(pluginOptions, args, file.extension, { - width: defaultBase64Width(), + width: pluginOptions.base64Width, }) let pipeline try { @@ -558,7 +556,7 @@ async function fluid({ file, args = {}, reporter, cache }) { let base64Image if (options.base64) { - const base64Width = options.base64Width || defaultBase64Width() + const base64Width = options.base64Width const base64Height = Math.max( 1, Math.round(base64Width / images[0].aspectRatio) @@ -693,7 +691,7 @@ async function fixed({ file, args = {}, reporter, cache }) { let base64Image if (options.base64) { - const base64Width = options.base64Width || defaultBase64Width() + const base64Width = options.base64Width const base64Height = Math.max( 1, Math.round(base64Width / images[0].aspectRatio) diff --git a/packages/gatsby-plugin-sharp/src/plugin-options.js b/packages/gatsby-plugin-sharp/src/plugin-options.js index b3a5bab3bcaf5..901491819c32b 100644 --- a/packages/gatsby-plugin-sharp/src/plugin-options.js +++ b/packages/gatsby-plugin-sharp/src/plugin-options.js @@ -2,7 +2,8 @@ const _ = require(`lodash`) /// Plugin options are loaded onPreBootstrap in gatsby-node const pluginDefaults = { - forceBase64Format: false, + base64Width: 20, + forceBase64Format: false, // valid formats: png,jpg,webp useMozJpeg: process.env.GATSBY_JPEG_ENCODER === `MOZJPEG`, stripMetadata: true, lazyImageGeneration: true, From 67e81286bc8300ef904874703e3a881ab70fc60c Mon Sep 17 00:00:00 2001 From: polarathene Date: Mon, 15 Jun 2020 16:37:25 +1200 Subject: [PATCH 2/9] chore: Change `forceBase64Format` default bool to empty string Using a default of `false` is a bit misleading as the expected values are `jpg`,`png` and `webp`, not `true`. The empty string is a falsey value, serving the same purpose. This matches the defaults for similar properties in general args. Additionally updates the docs to show the actual default values and missing options for plugin config options, as these lacked public documentation. --- packages/gatsby-plugin-sharp/README.md | 8 ++++++-- packages/gatsby-plugin-sharp/src/plugin-options.js | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/packages/gatsby-plugin-sharp/README.md b/packages/gatsby-plugin-sharp/README.md index 0b1c664d6478d..ff6948e1e9da3 100644 --- a/packages/gatsby-plugin-sharp/README.md +++ b/packages/gatsby-plugin-sharp/README.md @@ -30,9 +30,13 @@ plugins: [ { resolve: `gatsby-plugin-sharp`, options: { - useMozJpeg: false, + // Available options and their defaults: + base64Width: 20, + forceBase64Format: ``, // valid formats: png,jpg,webp + useMozJpeg: process.env.GATSBY_JPEG_ENCODER === `MOZJPEG`, stripMetadata: true, - defaultQuality: 75, + lazyImageGeneration: true, + defaultQuality: 50, }, }, ] diff --git a/packages/gatsby-plugin-sharp/src/plugin-options.js b/packages/gatsby-plugin-sharp/src/plugin-options.js index 901491819c32b..d248c57ac867e 100644 --- a/packages/gatsby-plugin-sharp/src/plugin-options.js +++ b/packages/gatsby-plugin-sharp/src/plugin-options.js @@ -3,7 +3,7 @@ const _ = require(`lodash`) /// Plugin options are loaded onPreBootstrap in gatsby-node const pluginDefaults = { base64Width: 20, - forceBase64Format: false, // valid formats: png,jpg,webp + forceBase64Format: ``, // valid formats: png,jpg,webp useMozJpeg: process.env.GATSBY_JPEG_ENCODER === `MOZJPEG`, stripMetadata: true, lazyImageGeneration: true, From a024f0af59ae941a54b4307d7381b8f21ca61cef Mon Sep 17 00:00:00 2001 From: polarathene Date: Mon, 15 Jun 2020 16:39:52 +1200 Subject: [PATCH 3/9] docs: `toFormatBase64` and `base64Width` Documents these two parameters for graphql (and their global default override option equivalents via plugin config). --- packages/gatsby-plugin-sharp/README.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/packages/gatsby-plugin-sharp/README.md b/packages/gatsby-plugin-sharp/README.md index ff6948e1e9da3..880e737bac917 100644 --- a/packages/gatsby-plugin-sharp/README.md +++ b/packages/gatsby-plugin-sharp/README.md @@ -141,6 +141,8 @@ following: - `grayscale` (bool, default: false) - `duotone` (bool|obj, default: false) - `toFormat` (string, default: '') +- `toFormatBase64` (string, default: '') +- `base64Width` (int, default: 20) - `cropFocus` (string, default: 'ATTENTION') - `fit` (string, default: 'COVER') - `pngCompressionSpeed` (int, default: 4) @@ -151,6 +153,26 @@ following: Convert the source image to one of the following available options: `NO_CHANGE`, `JPG`, `PNG`, `WEBP`. +#### toFormatBase64 + +base64 image uses the image format from the source , or the value of `toFormat`. This setting allows a different image format instead, available options are: `JPG`, `PNG`, `WEBP`. + +`WEBP` allows for a smaller data size, allowing you to reduce your HTML size when transferring over the network, or to use a larger base64 placeholder width default for improved image placeholder quality. + +[Not all browsers support `WEBP`](https://caniuse.com/#feat=webp). It would be wasteful to include a fallback image format in this case. Consider also adding a `backgroundColor` placeholder as a fallback instead. + +The plugin config option `forceBase64Format` performs the equivalent functionality by default to all your base64 placeholders. `toFormatBase64` has a higher priority for base64 images that need to selectively use a different format. + +#### base64Width + +The width in pixels for your base64 placeholder to use. The height will also be adjusted based on the aspect ratio of the image. Use this to increase the image quality by allowing more pixels to be used at the expense of increasing the file size of the data to be transferred. + +The default for Gatsby is `20`px. This keeps the data size low enough to embed into the HTML document for immediate display on DOM loaded and avoids an additional network request. + +[Facebook](https://engineering.fb.com/android/the-technology-behind-preview-photos/) and [Medium](https://jmperezperez.com/medium-image-progressive-loading-placeholder/) are both known to use `42`px width for their image placeholders. However Medium presently uses a solid background color placeholder to load the page as fast as possible, followed by an image placeholder requested over the network instead of embedding it with base64. + +The plugin config has an equivalent option, allowing you to change the default for all base64 placeholders. This parameter option has a higher priority over the plugin config option. + #### cropFocus Change the cropping focus. Available options: `CENTER`, `NORTH`, `NORTHEAST`, From 2adbcd88d6240b792eb6b39ed0406597d30cd9f6 Mon Sep 17 00:00:00 2001 From: polarathene Date: Mon, 15 Jun 2020 17:11:20 +1200 Subject: [PATCH 4/9] chore: Replace `args` with `options` `args` is a parameter, it should not have it's props modified, referencing the `options` object instead where `toFormat` may be modified. Rename the `forceBase64Format` variable to `changedBase64Format`. Should be less likely to imply a boolean, and isn't necessarily the same value as `forceBase64Format` stored on the `pluginOptions`. --- packages/gatsby-plugin-sharp/src/index.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/gatsby-plugin-sharp/src/index.js b/packages/gatsby-plugin-sharp/src/index.js index a3864dde64565..48389ea2e26c8 100644 --- a/packages/gatsby-plugin-sharp/src/index.js +++ b/packages/gatsby-plugin-sharp/src/index.js @@ -304,10 +304,10 @@ async function generateBase64({ file, args = {}, reporter }) { pipeline = pipeline.trim(options.trim) } - const forceBase64Format = - args.toFormatBase64 || pluginOptions.forceBase64Format - if (forceBase64Format) { - args.toFormat = forceBase64Format + const changedBase64Format = + options.toFormatBase64 || pluginOptions.forceBase64Format + if (changedBase64Format) { + options.toFormat = changedBase64Format } pipeline @@ -321,16 +321,16 @@ async function generateBase64({ file, args = {}, reporter }) { .png({ compressionLevel: options.pngCompressionLevel, adaptiveFiltering: false, - force: args.toFormat === `png`, + force: options.toFormat === `png`, }) .jpeg({ quality: options.jpegQuality || options.quality, progressive: options.jpegProgressive, - force: args.toFormat === `jpg`, + force: options.toFormat === `jpg`, }) .webp({ quality: options.webpQuality || options.quality, - force: args.toFormat === `webp`, + force: options.toFormat === `webp`, }) // grayscale @@ -345,7 +345,7 @@ async function generateBase64({ file, args = {}, reporter }) { // duotone if (options.duotone) { - pipeline = await duotone(options.duotone, args.toFormat, pipeline) + pipeline = await duotone(options.duotone, options.toFormat, pipeline) } const { data: buffer, info } = await pipeline.toBuffer({ resolveWithObject: true, From 55600b4c355934f4de40b426dc1dbaac099a4a2c Mon Sep 17 00:00:00 2001 From: Ward Peeters Date: Fri, 3 Jul 2020 09:02:42 +0200 Subject: [PATCH 5/9] Update packages/gatsby-plugin-sharp/README.md --- packages/gatsby-plugin-sharp/README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/gatsby-plugin-sharp/README.md b/packages/gatsby-plugin-sharp/README.md index 880e737bac917..dd9e8dc854007 100644 --- a/packages/gatsby-plugin-sharp/README.md +++ b/packages/gatsby-plugin-sharp/README.md @@ -35,7 +35,6 @@ plugins: [ forceBase64Format: ``, // valid formats: png,jpg,webp useMozJpeg: process.env.GATSBY_JPEG_ENCODER === `MOZJPEG`, stripMetadata: true, - lazyImageGeneration: true, defaultQuality: 50, }, }, From 364bbe5390cba6bf509550159bbd76705f37d2aa Mon Sep 17 00:00:00 2001 From: Brennan Kinney <5098581+polarathene@users.noreply.github.com> Date: Fri, 3 Jul 2020 23:33:23 +1200 Subject: [PATCH 6/9] fix:(gatsby-plugin-sharp): Update pluginOptions to new defaults --- .../src/__tests__/__snapshots__/index.js.snap | 75 ++++++++++++------- 1 file changed, 50 insertions(+), 25 deletions(-) diff --git a/packages/gatsby-plugin-sharp/src/__tests__/__snapshots__/index.js.snap b/packages/gatsby-plugin-sharp/src/__tests__/__snapshots__/index.js.snap index ec2715d39962b..c95306d26d17e 100644 --- a/packages/gatsby-plugin-sharp/src/__tests__/__snapshots__/index.js.snap +++ b/packages/gatsby-plugin-sharp/src/__tests__/__snapshots__/index.js.snap @@ -72,8 +72,9 @@ exports[`gatsby-plugin-sharp fixed correctly infers the width when only the heig }, ], "pluginOptions": Object { + "base64Width": 20, "defaultQuality": 50, - "forceBase64Format": false, + "forceBase64Format": "", "lazyImageGeneration": true, "stripMetadata": true, "useMozJpeg": false, @@ -126,8 +127,9 @@ exports[`gatsby-plugin-sharp fixed does not warn when the requested width is equ }, ], "pluginOptions": Object { + "base64Width": 20, "defaultQuality": 50, - "forceBase64Format": false, + "forceBase64Format": "", "lazyImageGeneration": true, "stripMetadata": true, "useMozJpeg": false, @@ -180,8 +182,9 @@ exports[`gatsby-plugin-sharp fixed should give the same result with createJob as }, ], "pluginOptions": Object { + "base64Width": 20, "defaultQuality": 50, - "forceBase64Format": false, + "forceBase64Format": "", "lazyImageGeneration": true, "stripMetadata": true, "useMozJpeg": false, @@ -220,8 +223,9 @@ exports[`gatsby-plugin-sharp fixed warns when the requested width is greater tha }, ], "pluginOptions": Object { + "base64Width": 20, "defaultQuality": 50, - "forceBase64Format": false, + "forceBase64Format": "", "lazyImageGeneration": true, "stripMetadata": true, "useMozJpeg": false, @@ -288,8 +292,9 @@ exports[`gatsby-plugin-sharp fluid accepts srcSet breakpoints 1`] = ` }, ], "pluginOptions": Object { + "base64Width": 20, "defaultQuality": 50, - "forceBase64Format": false, + "forceBase64Format": "", "lazyImageGeneration": true, "stripMetadata": true, "useMozJpeg": false, @@ -328,8 +333,9 @@ exports[`gatsby-plugin-sharp fluid adds pathPrefix if defined 1`] = ` }, ], "pluginOptions": Object { + "base64Width": 20, "defaultQuality": 50, - "forceBase64Format": false, + "forceBase64Format": "", "lazyImageGeneration": true, "stripMetadata": true, "useMozJpeg": false, @@ -408,8 +414,9 @@ Array [ }, ], "pluginOptions": Object { + "base64Width": 20, "defaultQuality": 50, - "forceBase64Format": false, + "forceBase64Format": "", "lazyImageGeneration": true, "stripMetadata": true, "useMozJpeg": false, @@ -487,8 +494,9 @@ Array [ }, ], "pluginOptions": Object { + "base64Width": 20, "defaultQuality": 50, - "forceBase64Format": false, + "forceBase64Format": "", "lazyImageGeneration": true, "stripMetadata": true, "useMozJpeg": false, @@ -566,8 +574,9 @@ Array [ }, ], "pluginOptions": Object { + "base64Width": 20, "defaultQuality": 50, - "forceBase64Format": false, + "forceBase64Format": "", "lazyImageGeneration": true, "stripMetadata": true, "useMozJpeg": false, @@ -645,8 +654,9 @@ Array [ }, ], "pluginOptions": Object { + "base64Width": 20, "defaultQuality": 50, - "forceBase64Format": false, + "forceBase64Format": "", "lazyImageGeneration": true, "stripMetadata": true, "useMozJpeg": false, @@ -702,8 +712,9 @@ Array [ }, ], "pluginOptions": Object { + "base64Width": 20, "defaultQuality": 50, - "forceBase64Format": false, + "forceBase64Format": "", "lazyImageGeneration": true, "stripMetadata": true, "useMozJpeg": false, @@ -763,8 +774,9 @@ Array [ }, ], "pluginOptions": Object { + "base64Width": 20, "defaultQuality": 50, - "forceBase64Format": false, + "forceBase64Format": "", "lazyImageGeneration": true, "stripMetadata": true, "useMozJpeg": false, @@ -824,8 +836,9 @@ Array [ }, ], "pluginOptions": Object { + "base64Width": 20, "defaultQuality": 50, - "forceBase64Format": false, + "forceBase64Format": "", "lazyImageGeneration": true, "stripMetadata": true, "useMozJpeg": false, @@ -885,8 +898,9 @@ Array [ }, ], "pluginOptions": Object { + "base64Width": 20, "defaultQuality": 50, - "forceBase64Format": false, + "forceBase64Format": "", "lazyImageGeneration": true, "stripMetadata": true, "useMozJpeg": false, @@ -918,8 +932,9 @@ exports[`gatsby-plugin-sharp fluid does not change the arguments object it is gi }, ], "pluginOptions": Object { + "base64Width": 20, "defaultQuality": 50, - "forceBase64Format": false, + "forceBase64Format": "", "lazyImageGeneration": true, "stripMetadata": true, "useMozJpeg": false, @@ -986,8 +1001,9 @@ exports[`gatsby-plugin-sharp fluid ensure maxWidth is in srcSet breakpoints 1`] }, ], "pluginOptions": Object { + "base64Width": 20, "defaultQuality": 50, - "forceBase64Format": false, + "forceBase64Format": "", "lazyImageGeneration": true, "stripMetadata": true, "useMozJpeg": false, @@ -1095,8 +1111,9 @@ exports[`gatsby-plugin-sharp fluid infers the maxWidth if only maxHeight is give }, ], "pluginOptions": Object { + "base64Width": 20, "defaultQuality": 50, - "forceBase64Format": false, + "forceBase64Format": "", "lazyImageGeneration": true, "stripMetadata": true, "useMozJpeg": false, @@ -1135,8 +1152,9 @@ exports[`gatsby-plugin-sharp fluid keeps original file name 1`] = ` }, ], "pluginOptions": Object { + "base64Width": 20, "defaultQuality": 50, - "forceBase64Format": false, + "forceBase64Format": "", "lazyImageGeneration": true, "stripMetadata": true, "useMozJpeg": false, @@ -1196,8 +1214,9 @@ exports[`gatsby-plugin-sharp fluid prevents duplicate breakpoints 1`] = ` }, ], "pluginOptions": Object { + "base64Width": 20, "defaultQuality": 50, - "forceBase64Format": false, + "forceBase64Format": "", "lazyImageGeneration": true, "stripMetadata": true, "useMozJpeg": false, @@ -1264,8 +1283,9 @@ exports[`gatsby-plugin-sharp fluid reject any breakpoints larger than the origin }, ], "pluginOptions": Object { + "base64Width": 20, "defaultQuality": 50, - "forceBase64Format": false, + "forceBase64Format": "", "lazyImageGeneration": true, "stripMetadata": true, "useMozJpeg": false, @@ -1304,8 +1324,9 @@ exports[`gatsby-plugin-sharp fluid should give the same result with createJob as }, ], "pluginOptions": Object { + "base64Width": 20, "defaultQuality": 50, - "forceBase64Format": false, + "forceBase64Format": "", "lazyImageGeneration": true, "stripMetadata": true, "useMozJpeg": false, @@ -1359,8 +1380,9 @@ exports[`gatsby-plugin-sharp queueImageResizing with createJob file name works w }, ], "pluginOptions": Object { + "base64Width": 20, "defaultQuality": 50, - "forceBase64Format": false, + "forceBase64Format": "", "lazyImageGeneration": true, "stripMetadata": true, "useMozJpeg": false, @@ -1401,8 +1423,9 @@ exports[`gatsby-plugin-sharp queueImageResizing with createJob should round heig }, ], "pluginOptions": Object { + "base64Width": 20, "defaultQuality": 50, - "forceBase64Format": false, + "forceBase64Format": "", "lazyImageGeneration": true, "stripMetadata": true, "useMozJpeg": false, @@ -1443,8 +1466,9 @@ exports[`gatsby-plugin-sharp queueImageResizing with createJobV2 file name works }, ], "pluginOptions": Object { + "base64Width": 20, "defaultQuality": 50, - "forceBase64Format": false, + "forceBase64Format": "", "lazyImageGeneration": true, "stripMetadata": true, "useMozJpeg": false, @@ -1483,8 +1507,9 @@ exports[`gatsby-plugin-sharp queueImageResizing with createJobV2 should round he }, ], "pluginOptions": Object { + "base64Width": 20, "defaultQuality": 50, - "forceBase64Format": false, + "forceBase64Format": "", "lazyImageGeneration": true, "stripMetadata": true, "useMozJpeg": false, From 49bc7046c20a3f6d6e8a05bbf3e960fbf9d7c863 Mon Sep 17 00:00:00 2001 From: polarathene <5098581+polarathene@users.noreply.github.com> Date: Tue, 14 Jul 2020 14:49:09 +1200 Subject: [PATCH 7/9] fix: Add new base64 tests `healOptions()` needed an update to pull in the `base64Width` default as fallback. base64 tests also needed modifications to avoid caching from calling `base64()` directly instead of via `fixed()` and `fluid()` methods which would invalidate cache and set `width` prop to `base64Width`. --- .../src/__tests__/index.js | 81 +++++++++++++++++++ packages/gatsby-plugin-sharp/src/index.js | 5 +- .../gatsby-plugin-sharp/src/plugin-options.js | 4 +- 3 files changed, 88 insertions(+), 2 deletions(-) diff --git a/packages/gatsby-plugin-sharp/src/__tests__/index.js b/packages/gatsby-plugin-sharp/src/__tests__/index.js index 36b9fef400f00..c506f019019ab 100644 --- a/packages/gatsby-plugin-sharp/src/__tests__/index.js +++ b/packages/gatsby-plugin-sharp/src/__tests__/index.js @@ -23,6 +23,7 @@ fs.existsSync = jest.fn().mockReturnValue(false) const { base64, + generateBase64, fluid, fixed, queueImageResizing, @@ -31,6 +32,11 @@ const { setBoundActionCreators, } = require(`../`) +const { + getPluginOptionsDefaults, + setPluginOptions, +} = require(`../plugin-options`) + jest.mock(`gatsby-cli/lib/reporter`, () => { return { log: jest.fn(), @@ -508,6 +514,81 @@ describe(`gatsby-plugin-sharp`, () => { expect(result).toEqual(result2) expect(result).not.toEqual(result3) }) + + // Uses 'generateBase64()` directly to avoid `base64()` caching affecting results. + it(`should have configurable width (base64Width)`, async () => { + const result = await generateBase64({ + file, + args: { base64Width: 42 }, + }) + + expect(result.width).toEqual(42) + }) + + it(`should support a different image format (toFormatBase64)`, async () => { + const result = await generateBase64({ + file, + args: { toFormatBase64: `webp` }, + }) + + expect(result.src).toEqual( + expect.stringMatching(/^data:image\/webp;base64/) + ) + }) + + // Testing similar base64 functionality, but via `gatsby-config.js` settings + // changing defaults instead of overrides via user args. + // Reset default options afterwards to avoid affecting other tests. + it(`should have configurable default width (base64Width)`, async () => { + setPluginOptions({ base64Width: 32 }) + + const result = await generateBase64({ + file, + args, + }) + + expect(result.width).toEqual(32) + setPluginOptions(getPluginOptionsDefaults()) + }) + + it(`should support a different default image format (forceBase64Format)`, async () => { + setPluginOptions({ forceBase64Format: `webp` }) + const result = await generateBase64({ + file, + args, + }) + + expect(result.src).toEqual( + expect.stringMatching(/^data:image\/webp;base64/) + ) + setPluginOptions(getPluginOptionsDefaults()) + }) + + // Args should have priority over configured defaults + it(`should have configurable width (base64Width) prioritized over default option`, async () => { + setPluginOptions({ base64Width: 32 }) + + const result = await generateBase64({ + file, + args: { base64Width: 42 }, + }) + + expect(result.width).toEqual(42) + setPluginOptions(getPluginOptionsDefaults()) + }) + + it(`should support a different image format (forceBase64Format) prioritized over default option`, async () => { + setPluginOptions({ forceBase64Format: `png` }) + const result = await generateBase64({ + file, + args: { toFormatBase64: `webp` }, + }) + + expect(result.src).toEqual( + expect.stringMatching(/^data:image\/webp;base64/) + ) + setPluginOptions(getPluginOptionsDefaults()) + }) }) describe(`image quirks`, () => { diff --git a/packages/gatsby-plugin-sharp/src/index.js b/packages/gatsby-plugin-sharp/src/index.js index 48389ea2e26c8..c0072cbe84aba 100644 --- a/packages/gatsby-plugin-sharp/src/index.js +++ b/packages/gatsby-plugin-sharp/src/index.js @@ -286,7 +286,9 @@ function batchQueueImageResizing({ file, transforms = [], reporter }) { async function generateBase64({ file, args = {}, reporter }) { const pluginOptions = getPluginOptions() const options = healOptions(pluginOptions, args, file.extension, { - width: pluginOptions.base64Width, + // Should already be set to base64Width by `fluid()`/`fixed()` methods + // calling `generateBase64()`. Useful in Jest tests still. + width: args.base64Width || pluginOptions.base64Width, }) let pipeline try { @@ -766,6 +768,7 @@ function toArray(buf) { exports.queueImageResizing = queueImageResizing exports.resize = queueImageResizing exports.base64 = base64 +exports.generateBase64 = generateBase64 exports.traceSVG = traceSVG exports.sizes = fluid exports.resolutions = fixed diff --git a/packages/gatsby-plugin-sharp/src/plugin-options.js b/packages/gatsby-plugin-sharp/src/plugin-options.js index d248c57ac867e..d024bd1dda8e1 100644 --- a/packages/gatsby-plugin-sharp/src/plugin-options.js +++ b/packages/gatsby-plugin-sharp/src/plugin-options.js @@ -38,6 +38,7 @@ exports.setPluginOptions = opts => { } exports.getPluginOptions = () => pluginOptions +exports.getPluginOptionsDefaults = () => pluginDefaults /** * Creates a transform object @@ -70,7 +71,7 @@ exports.createTransformObject = args => { } exports.healOptions = ( - { defaultQuality: quality }, + { defaultQuality: quality, base64Width }, args, fileExtension = ``, defaultArgs = {} @@ -81,6 +82,7 @@ exports.healOptions = ( options.pngCompressionSpeed = parseInt(options.pngCompressionSpeed, 10) options.toFormat = options.toFormat.toLowerCase() options.toFormatBase64 = options.toFormatBase64.toLowerCase() + options.base64Width = options.base64Width || base64Width // when toFormat is not set we set it based on fileExtension if (options.toFormat === ``) { From b5785067cf1ba5dfd2ab2fef7c642b8d6a382a76 Mon Sep 17 00:00:00 2001 From: polarathene <5098581+polarathene@users.noreply.github.com> Date: Tue, 14 Jul 2020 15:02:28 +1200 Subject: [PATCH 8/9] chore: Group `base64Width` and `toFormatBase64` tests separately --- .../src/__tests__/index.js | 112 +++++++++--------- 1 file changed, 56 insertions(+), 56 deletions(-) diff --git a/packages/gatsby-plugin-sharp/src/__tests__/index.js b/packages/gatsby-plugin-sharp/src/__tests__/index.js index c506f019019ab..748e94cc3f22d 100644 --- a/packages/gatsby-plugin-sharp/src/__tests__/index.js +++ b/packages/gatsby-plugin-sharp/src/__tests__/index.js @@ -515,79 +515,79 @@ describe(`gatsby-plugin-sharp`, () => { expect(result).not.toEqual(result3) }) - // Uses 'generateBase64()` directly to avoid `base64()` caching affecting results. - it(`should have configurable width (base64Width)`, async () => { - const result = await generateBase64({ - file, - args: { base64Width: 42 }, - }) - - expect(result.width).toEqual(42) - }) + describe(`base64Width option`, () => { + // Uses 'generateBase64()` directly to avoid `base64()` caching affecting results. + it(`should support a configurable width`, async () => { + const result = await generateBase64({ + file, + args: { base64Width: 42 }, + }) - it(`should support a different image format (toFormatBase64)`, async () => { - const result = await generateBase64({ - file, - args: { toFormatBase64: `webp` }, + expect(result.width).toEqual(42) }) - expect(result.src).toEqual( - expect.stringMatching(/^data:image\/webp;base64/) - ) - }) + it(`should support a configurable default width`, async () => { + setPluginOptions({ base64Width: 32 }) - // Testing similar base64 functionality, but via `gatsby-config.js` settings - // changing defaults instead of overrides via user args. - // Reset default options afterwards to avoid affecting other tests. - it(`should have configurable default width (base64Width)`, async () => { - setPluginOptions({ base64Width: 32 }) + const result = await generateBase64({ + file, + args, + }) - const result = await generateBase64({ - file, - args, + expect(result.width).toEqual(32) + setPluginOptions(getPluginOptionsDefaults()) }) - expect(result.width).toEqual(32) - setPluginOptions(getPluginOptionsDefaults()) - }) + it(`should prioritize a configurable width via arg over a configured default width`, async () => { + setPluginOptions({ base64Width: 32 }) - it(`should support a different default image format (forceBase64Format)`, async () => { - setPluginOptions({ forceBase64Format: `webp` }) - const result = await generateBase64({ - file, - args, - }) + const result = await generateBase64({ + file, + args: { base64Width: 42 }, + }) - expect(result.src).toEqual( - expect.stringMatching(/^data:image\/webp;base64/) - ) - setPluginOptions(getPluginOptionsDefaults()) + expect(result.width).toEqual(42) + setPluginOptions(getPluginOptionsDefaults()) + }) }) - // Args should have priority over configured defaults - it(`should have configurable width (base64Width) prioritized over default option`, async () => { - setPluginOptions({ base64Width: 32 }) + describe(`toFormatBase64 & forceBase64Format options`, () => { + it(`should support a different image format for base64`, async () => { + const result = await generateBase64({ + file, + args: { toFormatBase64: `webp` }, + }) - const result = await generateBase64({ - file, - args: { base64Width: 42 }, + expect(result.src).toEqual( + expect.stringMatching(/^data:image\/webp;base64/) + ) }) - expect(result.width).toEqual(42) - setPluginOptions(getPluginOptionsDefaults()) - }) + it(`should support a configurable default base64 image format`, async () => { + setPluginOptions({ forceBase64Format: `webp` }) + const result = await generateBase64({ + file, + args, + }) - it(`should support a different image format (forceBase64Format) prioritized over default option`, async () => { - setPluginOptions({ forceBase64Format: `png` }) - const result = await generateBase64({ - file, - args: { toFormatBase64: `webp` }, + expect(result.src).toEqual( + expect.stringMatching(/^data:image\/webp;base64/) + ) + setPluginOptions(getPluginOptionsDefaults()) }) - expect(result.src).toEqual( - expect.stringMatching(/^data:image\/webp;base64/) - ) - setPluginOptions(getPluginOptionsDefaults()) + it(`should prioritize a different base64 image format via arg over a configured default base64 image format`, async () => { + setPluginOptions({ forceBase64Format: `png` }) + const result = await generateBase64({ + file, + args: { toFormatBase64: `webp` }, + }) + + expect(result.src).toEqual( + expect.stringMatching(/^data:image\/webp;base64/) + ) + setPluginOptions(getPluginOptionsDefaults()) + }) }) }) From 1419afe21a48fc4d75909e054760693fadc8675f Mon Sep 17 00:00:00 2001 From: polarathene <5098581+polarathene@users.noreply.github.com> Date: Sun, 19 Jul 2020 16:53:52 +1200 Subject: [PATCH 9/9] fix: Support background colour option for base64 too Important for transparent images like PNG when converted to base64 image format like JPG where transparency is not supported. Test is fairly basic relying on snapshot but should be sufficient to indicate if something goes wrong. Could possibly benefit with tests on fluid/fixed where the actual bug was from them not passing the option down. --- .../src/__tests__/__snapshots__/index.js.snap | 10 +++++++++ .../src/__tests__/index.js | 22 +++++++++++++++---- packages/gatsby-plugin-sharp/src/index.js | 2 ++ 3 files changed, 30 insertions(+), 4 deletions(-) diff --git a/packages/gatsby-plugin-sharp/src/__tests__/__snapshots__/index.js.snap b/packages/gatsby-plugin-sharp/src/__tests__/__snapshots__/index.js.snap index c95306d26d17e..b43dd07b2090b 100644 --- a/packages/gatsby-plugin-sharp/src/__tests__/__snapshots__/index.js.snap +++ b/packages/gatsby-plugin-sharp/src/__tests__/__snapshots__/index.js.snap @@ -10,6 +10,16 @@ Object { } `; +exports[`gatsby-plugin-sharp base64 should support option: 'background' 1`] = ` +Object { + "aspectRatio": 1, + "height": 20, + "originalName": "alphatest.png", + "src": "data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAUABQDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAb/xAAUAQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIQAxAAAAGfAAAB/8QAFBABAAAAAAAAAAAAAAAAAAAAMP/aAAgBAQABBQIf/8QAFBEBAAAAAAAAAAAAAAAAAAAAIP/aAAgBAwEBPwEf/8QAFBEBAAAAAAAAAAAAAAAAAAAAIP/aAAgBAgEBPwEf/8QAFBABAAAAAAAAAAAAAAAAAAAAMP/aAAgBAQAGPwIf/8QAFBABAAAAAAAAAAAAAAAAAAAAMP/aAAgBAQABPyEf/9oADAMBAAIAAwAAABAAAAD/xAAUEQEAAAAAAAAAAAAAAAAAAAAg/9oACAEDAQE/EB//xAAUEQEAAAAAAAAAAAAAAAAAAAAg/9oACAECAQE/EB//xAAUEAEAAAAAAAAAAAAAAAAAAAAw/9oACAEBAAE/EB//2Q==", + "width": 20, +} +`; + exports[`gatsby-plugin-sharp duotone fixed 1`] = ` Object { "aspectRatio": 1, diff --git a/packages/gatsby-plugin-sharp/src/__tests__/index.js b/packages/gatsby-plugin-sharp/src/__tests__/index.js index 748e94cc3f22d..879999dea0d37 100644 --- a/packages/gatsby-plugin-sharp/src/__tests__/index.js +++ b/packages/gatsby-plugin-sharp/src/__tests__/index.js @@ -515,7 +515,21 @@ describe(`gatsby-plugin-sharp`, () => { expect(result).not.toEqual(result3) }) - describe(`base64Width option`, () => { + // Matches base64 string in snapshot, converts to jpg to force use of bg + // Testing pixel colour for a match would be better + it(`should support option: 'background'`, async () => { + const result = await generateBase64({ + file: getFileObject(path.join(__dirname, `images/alphatest.png`)), + args: { + background: `#ff0000`, + toFormatBase64: `jpg`, + }, + }) + + expect(result).toMatchSnapshot() + }) + + describe(`should support option: 'base64Width'`, () => { // Uses 'generateBase64()` directly to avoid `base64()` caching affecting results. it(`should support a configurable width`, async () => { const result = await generateBase64({ @@ -538,7 +552,7 @@ describe(`gatsby-plugin-sharp`, () => { setPluginOptions(getPluginOptionsDefaults()) }) - it(`should prioritize a configurable width via arg over a configured default width`, async () => { + it(`width via arg overrides global default`, async () => { setPluginOptions({ base64Width: 32 }) const result = await generateBase64({ @@ -551,7 +565,7 @@ describe(`gatsby-plugin-sharp`, () => { }) }) - describe(`toFormatBase64 & forceBase64Format options`, () => { + describe(`should support options: 'toFormatBase64' and 'forceBase64Format'`, () => { it(`should support a different image format for base64`, async () => { const result = await generateBase64({ file, @@ -576,7 +590,7 @@ describe(`gatsby-plugin-sharp`, () => { setPluginOptions(getPluginOptionsDefaults()) }) - it(`should prioritize a different base64 image format via arg over a configured default base64 image format`, async () => { + it(`image format via arg overrides global default`, async () => { setPluginOptions({ forceBase64Format: `png` }) const result = await generateBase64({ file, diff --git a/packages/gatsby-plugin-sharp/src/index.js b/packages/gatsby-plugin-sharp/src/index.js index c0072cbe84aba..dc182805e6ebb 100644 --- a/packages/gatsby-plugin-sharp/src/index.js +++ b/packages/gatsby-plugin-sharp/src/index.js @@ -564,6 +564,7 @@ async function fluid({ file, args = {}, reporter, cache }) { Math.round(base64Width / images[0].aspectRatio) ) const base64Args = { + background: options.background, duotone: options.duotone, grayscale: options.grayscale, rotate: options.rotate, @@ -699,6 +700,7 @@ async function fixed({ file, args = {}, reporter, cache }) { Math.round(base64Width / images[0].aspectRatio) ) const base64Args = { + background: options.background, duotone: options.duotone, grayscale: options.grayscale, rotate: options.rotate,