diff --git a/e2e-tests/contentful/cypress/integration/gatsby-plugin-image.js b/e2e-tests/contentful/cypress/integration/gatsby-plugin-image.js index 1894fdc4a6134..1678cea7af2d2 100644 --- a/e2e-tests/contentful/cypress/integration/gatsby-plugin-image.js +++ b/e2e-tests/contentful/cypress/integration/gatsby-plugin-image.js @@ -78,7 +78,7 @@ describe(`gatsby-plugin-image`, () => { testGatsbyPluginImage(`dominant-color`, hasColorPlaceholder) ) it(`traced`, testConfig, () => - testGatsbyPluginImage(`traced`, hasSVGPlaceholder) + testGatsbyPluginImage(`traced`, hasColorPlaceholder) ) it(`blurred`, testConfig, () => testGatsbyPluginImage(`blurred`, hasBase64Placeholder) diff --git a/e2e-tests/contentful/cypress/snapshots/gatsby-plugin-image.js/english-0.snap.png b/e2e-tests/contentful/cypress/snapshots/gatsby-plugin-image.js/english-0.snap.png index 263e5595d6eb4..2fabcb0cced11 100644 Binary files a/e2e-tests/contentful/cypress/snapshots/gatsby-plugin-image.js/english-0.snap.png and b/e2e-tests/contentful/cypress/snapshots/gatsby-plugin-image.js/english-0.snap.png differ diff --git a/e2e-tests/contentful/snapshots.js b/e2e-tests/contentful/snapshots.js index 2aeca7312618f..273c556c6caef 100644 --- a/e2e-tests/contentful/snapshots.js +++ b/e2e-tests/contentful/snapshots.js @@ -1,5 +1,5 @@ module.exports = { - "__version": "9.6.1", + "__version": "9.7.0", "content-reference": { "content-reference-many-2nd-level-loop": { "1": "
\n

Content Reference: Many (2nd level loop)

\n

[ContentfulNumber]\n 42

\n

[ContentfulText]\n The quick brown fox jumps over the lazy dog.

\n

[ContentfulReference]\n Content Reference: One (Loop A -> B)\n : [\n Content Reference: One (Loop B -> A)\n ]

\n
" diff --git a/e2e-tests/contentful/src/pages/gatsby-plugin-image.js b/e2e-tests/contentful/src/pages/gatsby-plugin-image.js index b444a2080f300..ead0cc27082f0 100644 --- a/e2e-tests/contentful/src/pages/gatsby-plugin-image.js +++ b/e2e-tests/contentful/src/pages/gatsby-plugin-image.js @@ -94,7 +94,9 @@ const GatsbyPluginImagePage = ({ data }) => { ))} -

gatsby-plugin-image: Traced SVG Placeholder

+

+ gatsby-plugin-image: Traced SVG Placeholder (fallback to DOMINANT_COLOR) +

{data.default.nodes.map(node => (
diff --git a/e2e-tests/development-runtime/cypress/integration/remote-file/gatsby-plugin-image.js b/e2e-tests/development-runtime/cypress/integration/remote-file/gatsby-plugin-image.js index 2fac5405bdca6..f5e49c77452ef 100644 --- a/e2e-tests/development-runtime/cypress/integration/remote-file/gatsby-plugin-image.js +++ b/e2e-tests/development-runtime/cypress/integration/remote-file/gatsby-plugin-image.js @@ -126,8 +126,9 @@ describe(`remote-file`, () => { cy.get(".constrained_traced [data-placeholder-image]") .first() .should($el => { - expect($el.prop("tagName")).to.be.equal("IMG") - expect($el.prop("src")).to.contain("data:image/svg+xml,%3csvg") + // traced falls back to DOMINANT_COLOR + expect($el.prop("tagName")).to.be.equal("DIV") + expect($el).to.be.empty }) cy.get(".full [data-placeholder-image]") .first() diff --git a/e2e-tests/development-runtime/cypress/integration/static-image/traced.js b/e2e-tests/development-runtime/cypress/integration/static-image/traced.js index c038f779a1a2c..0efda1ea4fef8 100644 --- a/e2e-tests/development-runtime/cypress/integration/static-image/traced.js +++ b/e2e-tests/development-runtime/cypress/integration/static-image/traced.js @@ -5,12 +5,14 @@ describe(`fixed`, () => { cy.visit(`/static-image/traced`).waitForRouteChange() }) - it(`renders a traced svg`, () => { + it(`traced svg (falls back to DOMINANT_COLOR)`, () => { cy.getTestElement(tracedTestId) - .find(`.gatsby-image-wrapper > img`) - .should(`have.attr`, `src`) - .and(src => { - ;[`data:image/svg+xml`].forEach(part => expect(src).to.include(part)) + .find(`.gatsby-image-wrapper > [data-placeholder-image]`) + .first() + .should($el => { + // traced falls + expect($el.prop("tagName")).to.be.equal("DIV") + expect($el).to.be.empty }) }) diff --git a/e2e-tests/production-runtime/cypress/integration/remote-file.js b/e2e-tests/production-runtime/cypress/integration/remote-file.js index 4ba72ca178b5a..ad5595a4e3bf2 100644 --- a/e2e-tests/production-runtime/cypress/integration/remote-file.js +++ b/e2e-tests/production-runtime/cypress/integration/remote-file.js @@ -1,149 +1,158 @@ -Cypress.on('uncaught:exception', (err) => { - if ((err.message.includes('Minified React error #418') || err.message.includes('Minified React error #423') || err.message.includes('Minified React error #425')) && Cypress.env(`TEST_PLUGIN_OFFLINE`)) { +Cypress.on("uncaught:exception", err => { + if ( + (err.message.includes("Minified React error #418") || + err.message.includes("Minified React error #423") || + err.message.includes("Minified React error #425")) && + Cypress.env(`TEST_PLUGIN_OFFLINE`) + ) { return false } }) describe( - `remote-file`, + `remote-file`, { retries: { runMode: 4, }, }, () => { - beforeEach(() => { - cy.visit(`/remote-file/`).waitForRouteChange() + beforeEach(() => { + cy.visit(`/remote-file/`).waitForRouteChange() - // trigger intersection observer - cy.scrollTo("top") - cy.wait(100) - cy.scrollTo("bottom", { - duration: 500, - }) - cy.wait(500) - }) - - async function testImages(images, expectations) { - for (let i = 0; i < images.length; i++) { - const expectation = expectations[i] - - const res = await fetch(images[i].currentSrc, { - method: "HEAD", + // trigger intersection observer + cy.scrollTo("top") + cy.wait(100) + cy.scrollTo("bottom", { + duration: 500, }) - expect(res.ok).to.be.true - if (expectation.width) { - expect(Math.ceil(images[i].getBoundingClientRect().width)).to.be.equal( - expectation.width - ) - } - if (expectation.height) { - expect(Math.ceil(images[i].getBoundingClientRect().height)).to.be.equal( - expectation.height - ) - } - } - } + cy.wait(500) + }) - it(`should render correct dimensions`, () => { - cy.get('[data-testid="public"]').then(async $urls => { - const urls = Array.from($urls.map((_, $url) => $url.getAttribute("href"))) + async function testImages(images, expectations) { + for (let i = 0; i < images.length; i++) { + const expectation = expectations[i] - for (const url of urls) { - const res = await fetch(url, { + const res = await fetch(images[i].currentSrc, { method: "HEAD", }) expect(res.ok).to.be.true + if (expectation.width) { + expect( + Math.ceil(images[i].getBoundingClientRect().width) + ).to.be.equal(expectation.width) + } + if (expectation.height) { + expect( + Math.ceil(images[i].getBoundingClientRect().height) + ).to.be.equal(expectation.height) + } } - }) - - cy.get(".resize").then(async $imgs => { - await testImages(Array.from($imgs), [ - { - width: 100, - height: 133, - }, - { - width: 100, - height: 160, - }, - { - width: 100, - height: 67, - }, - ]) - }) + } - cy.get(".fixed").then(async $imgs => { - await testImages(Array.from($imgs), [ - { - width: 100, - height: 133, - }, - { - width: 100, - height: 160, - }, - { - width: 100, - height: 67, - }, - ]) - }) + it(`should render correct dimensions`, () => { + cy.get('[data-testid="public"]').then(async $urls => { + const urls = Array.from( + $urls.map((_, $url) => $url.getAttribute("href")) + ) - cy.get(".constrained").then(async $imgs => { - await testImages(Array.from($imgs), [ - { - width: 300, - height: 400, - }, - { - width: 300, - height: 481, - }, - { - width: 300, - height: 200, - }, - ]) - }) + for (const url of urls) { + const res = await fetch(url, { + method: "HEAD", + }) + expect(res.ok).to.be.true + } + }) - cy.get(".full").then(async $imgs => { - await testImages(Array.from($imgs), [ - { - height: 1229, - }, - { - height: 1478, - }, - { - height: 614, - }, - ]) - }) - }) + cy.get(".resize").then(async $imgs => { + await testImages(Array.from($imgs), [ + { + width: 100, + height: 133, + }, + { + width: 100, + height: 160, + }, + { + width: 100, + height: 67, + }, + ]) + }) - it(`should render a placeholder`, () => { - cy.get(".fixed [data-placeholder-image]") - .first() - .should("have.css", "background-color", "rgb(232, 184, 8)") - cy.get(".constrained [data-placeholder-image]") - .first() - .should($el => { - expect($el.prop("tagName")).to.be.equal("IMG") - expect($el.prop("src")).to.contain("data:image/jpg;base64") + cy.get(".fixed").then(async $imgs => { + await testImages(Array.from($imgs), [ + { + width: 100, + height: 133, + }, + { + width: 100, + height: 160, + }, + { + width: 100, + height: 67, + }, + ]) }) - cy.get(".constrained_traced [data-placeholder-image]") - .first() - .should($el => { - expect($el.prop("tagName")).to.be.equal("IMG") - expect($el.prop("src")).to.contain("data:image/svg+xml,%3csvg") + + cy.get(".constrained").then(async $imgs => { + await testImages(Array.from($imgs), [ + { + width: 300, + height: 400, + }, + { + width: 300, + height: 481, + }, + { + width: 300, + height: 200, + }, + ]) }) - cy.get(".full [data-placeholder-image]") - .first() - .should($el => { - expect($el.prop("tagName")).to.be.equal("DIV") - expect($el).to.be.empty + + cy.get(".full").then(async $imgs => { + await testImages(Array.from($imgs), [ + { + height: 1229, + }, + { + height: 1478, + }, + { + height: 614, + }, + ]) }) - }) -}) + }) + + it(`should render a placeholder`, () => { + cy.get(".fixed [data-placeholder-image]") + .first() + .should("have.css", "background-color", "rgb(232, 184, 8)") + cy.get(".constrained [data-placeholder-image]") + .first() + .should($el => { + expect($el.prop("tagName")).to.be.equal("IMG") + expect($el.prop("src")).to.contain("data:image/jpg;base64") + }) + cy.get(".constrained_traced [data-placeholder-image]") + .first() + .should($el => { + // traced falls back to DOMINANT_COLOR + expect($el.prop("tagName")).to.be.equal("DIV") + expect($el).to.be.empty + }) + cy.get(".full [data-placeholder-image]") + .first() + .should($el => { + expect($el.prop("tagName")).to.be.equal("DIV") + expect($el).to.be.empty + }) + }) + } +) diff --git a/e2e-tests/production-runtime/cypress/integration/static-image/traced.js b/e2e-tests/production-runtime/cypress/integration/static-image/traced.js index a030714387422..e364290316848 100644 --- a/e2e-tests/production-runtime/cypress/integration/static-image/traced.js +++ b/e2e-tests/production-runtime/cypress/integration/static-image/traced.js @@ -1,7 +1,12 @@ const tracedTestId = `image-traced` -Cypress.on('uncaught:exception', (err) => { - if ((err.message.includes('Minified React error #418') || err.message.includes('Minified React error #423') || err.message.includes('Minified React error #425')) && Cypress.env(`TEST_PLUGIN_OFFLINE`)) { +Cypress.on("uncaught:exception", err => { + if ( + (err.message.includes("Minified React error #418") || + err.message.includes("Minified React error #423") || + err.message.includes("Minified React error #425")) && + Cypress.env(`TEST_PLUGIN_OFFLINE`) + ) { return false } }) @@ -11,12 +16,14 @@ describe(`fixed`, () => { cy.visit(`/static-image/traced`).waitForRouteChange() }) - it(`renders a traced svg`, () => { + it(`traced svg (falls back to DOMINANT_COLOR)`, () => { cy.getTestElement(tracedTestId) - .find(`.gatsby-image-wrapper > img`) - .should(`have.attr`, `src`) - .and(src => { - ;[`data:image/svg+xml`].forEach(part => expect(src).to.include(part)) + .find(`.gatsby-image-wrapper > [data-placeholder-image]`) + .first() + .should($el => { + // traced falls + expect($el.prop("tagName")).to.be.equal("DIV") + expect($el).to.be.empty }) }) diff --git a/examples/using-contentful/package.json b/examples/using-contentful/package.json index 4a04b83b91de1..f1a66f4698077 100644 --- a/examples/using-contentful/package.json +++ b/examples/using-contentful/package.json @@ -30,6 +30,7 @@ "scripts": { "develop": "gatsby develop", "build": "gatsby build", + "clean": "gatsby clean", "start": "gatsby serve" } -} \ No newline at end of file +} diff --git a/packages/gatsby-plugin-image/src/resolver-utils.ts b/packages/gatsby-plugin-image/src/resolver-utils.ts index 513b6daacca9d..5852d0e76d739 100644 --- a/packages/gatsby-plugin-image/src/resolver-utils.ts +++ b/packages/gatsby-plugin-image/src/resolver-utils.ts @@ -202,9 +202,9 @@ export function getGatsbyImageFieldConfig( type: ImagePlaceholderType.name, description: stripIndent` Format of generated placeholder image, displayed while the main image loads. - BLURRED: a blurred, low resolution image, encoded as a base64 data URI (default) - DOMINANT_COLOR: a solid color, calculated from the dominant color of the image. - TRACED_SVG: a low-resolution traced SVG of the image. + BLURRED: a blurred, low resolution image, encoded as a base64 data URI. + DOMINANT_COLOR: a solid color, calculated from the dominant color of the image (default). + TRACED_SVG: deprecated. Will use DOMINANT_COLOR. NONE: no placeholder. Set the argument "backgroundColor" to use a fixed background color.`, }, formats: { diff --git a/packages/gatsby-plugin-sharp/package.json b/packages/gatsby-plugin-sharp/package.json index a8a1313b8bdf2..b7b17d444567e 100644 --- a/packages/gatsby-plugin-sharp/package.json +++ b/packages/gatsby-plugin-sharp/package.json @@ -8,7 +8,6 @@ }, "dependencies": { "@babel/runtime": "^7.15.4", - "@gatsbyjs/potrace": "^2.3.0", "async": "^3.2.4", "bluebird": "^3.7.2", "debug": "^4.3.4", @@ -17,11 +16,9 @@ "gatsby-core-utils": "^4.2.0-next.0", "gatsby-plugin-utils": "^4.2.0-next.0", "lodash": "^4.17.21", - "mini-svg-data-uri": "^1.4.4", "probe-image-size": "^7.2.3", "semver": "^7.3.7", - "sharp": "^0.30.7", - "svgo": "^2.8.0" + "sharp": "^0.30.7" }, "devDependencies": { "@babel/cli": "^7.15.4", 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 ea17e500aba82..4036b74d4cb8c 100644 --- a/packages/gatsby-plugin-sharp/src/__tests__/__snapshots__/index.js.snap +++ b/packages/gatsby-plugin-sharp/src/__tests__/__snapshots__/index.js.snap @@ -1397,7 +1397,7 @@ Object { } `; -exports[`gatsby-plugin-sharp tracedSVG runs on demand 1`] = ` +exports[`gatsby-plugin-sharp tracedSVG runs on demand (and falls back to blurred): fixed 1`] = ` Object { "aspectRatio": 1, "base64": undefined, @@ -1405,12 +1405,12 @@ Object { "originalName": "test.png", "src": "/static/1234/7e516/test.png", "srcSet": "/static/1234/7e516/test.png 1x", - "tracedSVG": "data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20width='100'%20height='100'%20viewBox='0%200%20100%20100'%20preserveAspectRatio='none'%3e%3cpath%20d='M41%2024c-18%207-24%2029-11%2043%2015%2017%2044%208%2046-15%201-19-17-34-35-28'%20fill='red'%20fill-rule='evenodd'/%3e%3c/svg%3e", + "tracedSVG": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAIAAAAC64paAAAACXBIWXMAAAsTAAALEwEAmpwYAAABP0lEQVQ4y2P4r8ZANmKgvuZ/agy/VaHojyopmv+CNf/XQCCoCEHNf1VBqv+oM5yVY1giwbBakuG2AkjknzoW/eh2/tdgOCPHYM7OwMDAwAgiGFgYGMJ5GF4og43ApRmuk58JqpMJRjIwMBizMbxTZviPaj8ihCD6rThAStkgljJATWEHc3P5wT5SxdD8B2ztERmQIiYGdAAxSpaF4T2q5TDN4HCaLgZSxMyAE1yQgwY+Fs1zxQloviSPTTMktM7JgVzIiKEH4hElFoavKogAQgltiJA3F0gdOyPC58yw8GsUwhFgUMvVGR4oMqiwogQbhOHLxfBLDcVabIlEneGxEkMMLwMvTLc4M0OdEMN3VfRIxp48/6mDnPdCieGkHCiRflRh+K+JPXljz1IQJ0AzhjrRGQPZC5As+ZeuhQGRmgHU8mT34D0STQAAAABJRU5ErkJggg==", "width": 100, } `; -exports[`gatsby-plugin-sharp tracedSVG runs on demand 2`] = ` +exports[`gatsby-plugin-sharp tracedSVG runs on demand (and falls back to blurred): fluid 1`] = ` Object { "aspectRatio": 1, "base64": undefined, @@ -1425,6 +1425,6 @@ Object { /static/1234/a1812/test.png 50w, /static/1234/7e516/test.png 100w", "srcSetType": "image/png", - "tracedSVG": "data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20width='100'%20height='100'%20viewBox='0%200%20100%20100'%20preserveAspectRatio='none'%3e%3cpath%20d='M41%2024c-18%207-24%2029-11%2043%2015%2017%2044%208%2046-15%201-19-17-34-35-28'%20fill='red'%20fill-rule='evenodd'/%3e%3c/svg%3e", + "tracedSVG": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAIAAAAC64paAAAACXBIWXMAAAsTAAALEwEAmpwYAAABP0lEQVQ4y2P4r8ZANmKgvuZ/agy/VaHojyopmv+CNf/XQCCoCEHNf1VBqv+oM5yVY1giwbBakuG2AkjknzoW/eh2/tdgOCPHYM7OwMDAwAgiGFgYGMJ5GF4og43ApRmuk58JqpMJRjIwMBizMbxTZviPaj8ihCD6rThAStkgljJATWEHc3P5wT5SxdD8B2ztERmQIiYGdAAxSpaF4T2q5TDN4HCaLgZSxMyAE1yQgwY+Fs1zxQloviSPTTMktM7JgVzIiKEH4hElFoavKogAQgltiJA3F0gdOyPC58yw8GsUwhFgUMvVGR4oMqiwogQbhOHLxfBLDcVabIlEneGxEkMMLwMvTLc4M0OdEMN3VfRIxp48/6mDnPdCieGkHCiRflRh+K+JPXljz1IQJ0AzhjrRGQPZC5As+ZeuhQGRmgHU8mT34D0STQAAAABJRU5ErkJggg==", } `; diff --git a/packages/gatsby-plugin-sharp/src/__tests__/index.js b/packages/gatsby-plugin-sharp/src/__tests__/index.js index e0713939fa4d2..38f7ec1ece1a0 100644 --- a/packages/gatsby-plugin-sharp/src/__tests__/index.js +++ b/packages/gatsby-plugin-sharp/src/__tests__/index.js @@ -588,7 +588,7 @@ describe(`gatsby-plugin-sharp`, () => { expect(result.tracedSVG).toBeUndefined() }) - it(`runs on demand`, async () => { + it(`runs on demand (and falls back to blurred)`, async () => { const args = { maxWidth: 100, width: 100, @@ -602,14 +602,20 @@ describe(`gatsby-plugin-sharp`, () => { args, }) - expect(fixedSvg).toMatchSnapshot() + expect(fixedSvg).toMatchSnapshot(`fixed`) + + expect(fixedSvg.tracedSVG).toMatch(`data:image/png;base64`) + expect(fixedSvg.tracedSVG).not.toMatch(`data:image/svg+xml`) const fluidSvg = await fluid({ file, args, }) - expect(fluidSvg).toMatchSnapshot() + expect(fluidSvg).toMatchSnapshot(`fluid`) + + expect(fluidSvg.tracedSVG).toMatch(`data:image/png;base64`) + expect(fluidSvg.tracedSVG).not.toMatch(`data:image/svg+xml`) }) }) diff --git a/packages/gatsby-plugin-sharp/src/__tests__/trace-svg.js b/packages/gatsby-plugin-sharp/src/__tests__/trace-svg.js deleted file mode 100644 index 11cf82ac17338..0000000000000 --- a/packages/gatsby-plugin-sharp/src/__tests__/trace-svg.js +++ /dev/null @@ -1,270 +0,0 @@ -jest.mock(`os`, () => { - const path = require(`path`) - - return { - ...jest.requireActual(`os`), - tmpdir: () => path.join(__dirname, `.cache`), - } -}) - -const path = require(`path`) -const fs = require(`fs-extra`) - -const traceSVGHelpers = require(`../trace-svg`) - -const notMemoizedtraceSVG = jest.spyOn(traceSVGHelpers, `notMemoizedtraceSVG`) -const notMemoizedPrepareTraceSVGInputFile = jest.spyOn( - traceSVGHelpers, - `notMemoizedPrepareTraceSVGInputFile` -) -// note that we started spying on not memoized functions first -// now we recreate memoized functions that will use function we just started -// spying on -traceSVGHelpers.createMemoizedFunctions() -const memoizedTraceSVG = jest.spyOn(traceSVGHelpers, `memoizedTraceSVG`) -const memoizedPrepareTraceSVGInputFile = jest.spyOn( - traceSVGHelpers, - `memoizedPrepareTraceSVGInputFile` -) - -const { traceSVG } = require(`../`) - -function getFileObject(absolutePath, name = path.parse(absolutePath).name) { - return { - id: `${absolutePath} absPath of file`, - name: name, - absolutePath, - extension: `png`, - internal: { - contentDigest: `2022-01-13T13:27:56.654Z`, - }, - } -} - -describe(`traceSVG memoization`, () => { - const file = getFileObject(path.join(__dirname, `images/test.png`)) - const differentFile = getFileObject( - path.join(__dirname, `images/144-density.png`) - ) - differentFile.internal.contentDigest = `4321` - - beforeAll(async () => { - await fs.ensureDir(path.join(__dirname, `.cache`)) - }) - - afterAll(async () => { - await fs.remove(path.join(__dirname, `.cache`)) - }) - - beforeEach(() => { - traceSVGHelpers.clearMemoizeCaches() - memoizedTraceSVG.mockClear() - notMemoizedtraceSVG.mockClear() - memoizedPrepareTraceSVGInputFile.mockClear() - notMemoizedPrepareTraceSVGInputFile.mockClear() - }) - - it(`Baseline`, async () => { - await traceSVG({ - file, - }) - - expect(memoizedTraceSVG).toBeCalledTimes(1) - expect(notMemoizedtraceSVG).toBeCalledTimes(1) - expect(memoizedPrepareTraceSVGInputFile).toBeCalledTimes(1) - expect(notMemoizedPrepareTraceSVGInputFile).toBeCalledTimes(1) - }) - - it(`should memoizing results for same args`, async () => { - await traceSVG({ - file, - }) - - await traceSVG({ - file, - }) - - expect(memoizedTraceSVG).toBeCalledTimes(2) - expect(notMemoizedtraceSVG).toBeCalledTimes(1) - expect(memoizedPrepareTraceSVGInputFile).toBeCalledTimes(1) - expect(notMemoizedPrepareTraceSVGInputFile).toBeCalledTimes(1) - }) - - it( - `should call functions with same input file when params change`, - async () => { - await traceSVG({ - file, - args: { - color: `red`, - }, - fileArgs: { - width: 400, - }, - }) - await traceSVG({ - file, - args: { - color: `blue`, - }, - fileArgs: { - width: 400, - }, - }) - await traceSVG({ - file, - args: { - color: `red`, - }, - fileArgs: { - width: 200, - }, - }) - await traceSVG({ - file, - args: { - color: `blue`, - }, - fileArgs: { - width: 200, - }, - }) - await traceSVG({ - file: differentFile, - args: { - color: `red`, - }, - fileArgs: { - width: 400, - }, - }) - - expect(memoizedTraceSVG).toBeCalledTimes(5) - expect(notMemoizedtraceSVG).toBeCalledTimes(5) - expect(memoizedPrepareTraceSVGInputFile).toBeCalledTimes(5) - // trace svg should be actually created just 3 times - // because it's affected just by `fileArgs`, and not `args` - // this makes sure we don't try to write to same input file multiple times - expect(notMemoizedPrepareTraceSVGInputFile).toBeCalledTimes(3) - expect(notMemoizedPrepareTraceSVGInputFile).toHaveBeenNthCalledWith( - 1, - expect.objectContaining({ - file, - options: expect.objectContaining({ - width: 400, - }), - }) - ) - expect(notMemoizedPrepareTraceSVGInputFile).toHaveBeenNthCalledWith( - 2, - expect.objectContaining({ - file, - options: expect.objectContaining({ - width: 200, - }), - }) - ) - expect(notMemoizedPrepareTraceSVGInputFile).toHaveBeenNthCalledWith( - 3, - expect.objectContaining({ - file: differentFile, - options: expect.objectContaining({ - width: 400, - }), - }) - ) - - const usedTmpFilePaths = - notMemoizedPrepareTraceSVGInputFile.mock.calls.map( - args => args[0].tmpFilePath - ) - - // tmpFilePath was always unique - expect(usedTmpFilePaths.length).toBe(new Set(usedTmpFilePaths).size) - }, - 10 * 1000 - ) - - it(`Use memoized results for file copies`, async () => { - const copyPath = path.join(__dirname, `images/test-copy.png`) - await fs.copy(path.join(__dirname, `images/test.png`), copyPath) - - try { - const copyOfFile = getFileObject(copyPath) - await traceSVG({ - file, - args: { - color: `red`, - }, - fileArgs: { - width: 400, - }, - }) - await traceSVG({ - file: copyOfFile, - args: { - color: `red`, - }, - fileArgs: { - width: 400, - }, - }) - } finally { - await fs.remove(copyPath) - } - - expect(memoizedTraceSVG).toBeCalledTimes(2) - expect(notMemoizedtraceSVG).toBeCalledTimes(1) - expect(memoizedPrepareTraceSVGInputFile).toBeCalledTimes(1) - expect(notMemoizedPrepareTraceSVGInputFile).toBeCalledTimes(1) - }) - - it(`should work with long filenames`, async () => { - const copyPath = path.join( - __dirname, - `images/${`a`.repeat(10)} (1) ${`a`.repeat(100)}.png` - ) - await fs.copy(path.join(__dirname, `images/test.png`), copyPath) - expect.assertions(1) - - try { - const copyOfFile = getFileObject(copyPath) - await traceSVG({ - file: copyOfFile, - args: { - color: `red`, - }, - fileArgs: { - width: 400, - }, - }) - expect(true).toBe(true) - } finally { - await fs.remove(copyPath) - } - }) - - it(`should work with long filenames that end with a dot`, async () => { - const copyPath = path.join(__dirname, `images/test${`.`.repeat(100)}.png`) - await fs.copy(path.join(__dirname, `images/test.png`), copyPath) - expect.assertions(1) - - try { - const copyOfFile = getFileObject(copyPath) - await traceSVG({ - file: copyOfFile, - args: { - color: `red`, - }, - fileArgs: { - width: 400, - }, - }) - expect(true).toBe(true) - } catch (err) { - await fs.remove(copyPath) - } finally { - await fs.remove(copyPath) - } - }) -}) diff --git a/packages/gatsby-plugin-sharp/src/image-data.ts b/packages/gatsby-plugin-sharp/src/image-data.ts index a6c34cb11ea02..81364e3cd0e7e 100644 --- a/packages/gatsby-plugin-sharp/src/image-data.ts +++ b/packages/gatsby-plugin-sharp/src/image-data.ts @@ -119,6 +119,7 @@ function normalizeFormat(format: string): ImageFormat { return format as ImageFormat } +let didShowTraceSVGRemovalWarning = false export async function generateImageData({ file, args, @@ -128,7 +129,7 @@ export async function generateImageData({ }: IImageDataArgs): Promise { args = mergeDefaults(args) - const { + let { layout = `constrained`, placeholder = `dominantColor`, tracedSVGOptions = {}, @@ -145,6 +146,16 @@ export async function generateImageData({ : DEFAULT_BREAKPOINTS } + if (placeholder === `tracedSVG`) { + if (!didShowTraceSVGRemovalWarning) { + console.warn( + `"TRACED_SVG" placeholder argument value is no longer supported (used in gatsbyImageData processing), falling back to "DOMINANT_COLOR". See https://gatsby.dev/tracesvg-removal/` + ) + didShowTraceSVGRemovalWarning = true + } + placeholder = `dominantColor` + } + const { fit = `cover`, cropFocus = sharp.strategy.attention, diff --git a/packages/gatsby-plugin-sharp/src/index.js b/packages/gatsby-plugin-sharp/src/index.js index f770efd3f5912..c1fef34709bbc 100644 --- a/packages/gatsby-plugin-sharp/src/index.js +++ b/packages/gatsby-plugin-sharp/src/index.js @@ -15,7 +15,6 @@ const { createTransformObject, removeDefaultValues, } = require(`./plugin-options`) -const { memoizedTraceSVG, notMemoizedtraceSVG } = require(`./trace-svg`) const duotone = require(`./duotone`) const { IMAGE_PROCESSING_JOB_NAME } = require(`./gatsby-worker`) const { getDimensionsAndAspectRatio } = require(`./utils`) @@ -382,26 +381,17 @@ async function base64(arg) { return await memoizedBase64(arg) } +let didShowTraceSVGRemovalWarning = false async function traceSVG(args) { - if (args.cache) { - // Not all transformer plugins are going to provide cache - return await cachifiedProcess(args, generateCacheKey, notMemoizedtraceSVG) + if (!didShowTraceSVGRemovalWarning) { + console.warn( + `traceSVG placeholder generation is no longer supported, falling back to blurred. See https://gatsby.dev/tracesvg-removal/` + ) + didShowTraceSVGRemovalWarning = true } - return await memoizedTraceSVG(args) -} -async function getTracedSVG({ file, options, cache, reporter }) { - if (options.generateTracedSVG && options.tracedSVG) { - const tracedSVG = await traceSVG({ - args: options.tracedSVG, - fileArgs: options, - file, - cache, - reporter, - }) - return tracedSVG - } - return undefined + const { src } = await base64(args) + return src } async function stats({ file, reporter }) { @@ -425,6 +415,7 @@ async function stats({ file, reporter }) { } } +let didShowTraceSVGRemovalWarningFluid = false async function fluid({ file, args = {}, reporter, cache }) { const options = healOptions(getPluginOptions(), args, file.extension) @@ -542,8 +533,17 @@ async function fluid({ file, args = {}, reporter, cache }) { reporter, }) + if (options.generateTracedSVG && options.tracedSVG) { + if (!didShowTraceSVGRemovalWarningFluid) { + console.warn( + `tracedSVG placeholder generation for fluid images is no longer supported, falling back to blurred. See https://gatsby.dev/tracesvg-removal/` + ) + didShowTraceSVGRemovalWarningFluid = true + } + } + let base64Image - if (options.base64) { + if (options.base64 || (options.generateTracedSVG && options.tracedSVG)) { const base64Width = options.base64Width const base64Height = Math.max( 1, @@ -566,8 +566,6 @@ async function fluid({ file, args = {}, reporter, cache }) { base64Image = await base64({ file, args: base64Args, reporter, cache }) } - const tracedSVG = await getTracedSVG({ options, file, cache, reporter }) - // Construct src and srcSet strings. const originalImg = _.maxBy(images, image => image.width).src const fallbackSrc = _.minBy(images, image => @@ -614,7 +612,7 @@ async function fluid({ file, args = {}, reporter, cache }) { `(max-width: ${presentationWidth}px) 100vw, ${presentationWidth}px` return { - base64: base64Image && base64Image.src, + base64: (options.base64 && base64Image && base64Image.src) || undefined, aspectRatio: images[0].aspectRatio, src: fallbackSrc, srcSet, @@ -625,10 +623,16 @@ async function fluid({ file, args = {}, reporter, cache }) { density, presentationWidth, presentationHeight, - tracedSVG, + tracedSVG: + (options.generateTracedSVG && + options.tracedSVG && + base64Image && + base64Image.src) || + undefined, } } +let didShowTraceSVGRemovalWarningFixed = false async function fixed({ file, args = {}, reporter, cache }) { const options = healOptions(getPluginOptions(), args, file.extension) @@ -681,8 +685,17 @@ async function fixed({ file, args = {}, reporter, cache }) { reporter, }) + if (options.generateTracedSVG && options.tracedSVG) { + if (!didShowTraceSVGRemovalWarningFixed) { + console.warn( + `tracedSVG placeholder generation for fixed images is no longer supported, falling back to blurred. See https://gatsby.dev/tracesvg-removal/` + ) + didShowTraceSVGRemovalWarningFixed = true + } + } + let base64Image - if (options.base64) { + if (options.base64 || (options.generateTracedSVG && options.tracedSVG)) { const base64Width = options.base64Width const base64Height = Math.max( 1, @@ -710,8 +723,6 @@ async function fixed({ file, args = {}, reporter, cache }) { }) } - const tracedSVG = await getTracedSVG({ options, file, reporter, cache }) - const fallbackSrc = images[0].src const srcSet = images .map((image, i) => { @@ -735,14 +746,19 @@ async function fixed({ file, args = {}, reporter, cache }) { const originalName = file.base return { - base64: base64Image && base64Image.src, + base64: (options.base64 && base64Image && base64Image.src) || undefined, aspectRatio: images[0].aspectRatio, width: images[0].width, height: images[0].height, src: fallbackSrc, srcSet, originalName: originalName, - tracedSVG, + tracedSVG: + (options.generateTracedSVG && + options.tracedSVG && + base64Image && + base64Image.src) || + undefined, } } diff --git a/packages/gatsby-plugin-sharp/src/trace-svg.js b/packages/gatsby-plugin-sharp/src/trace-svg.js deleted file mode 100644 index d216b192425a9..0000000000000 --- a/packages/gatsby-plugin-sharp/src/trace-svg.js +++ /dev/null @@ -1,187 +0,0 @@ -const { promisify } = require(`bluebird`) -const fs = require(`fs-extra`) -const _ = require(`lodash`) -const tmpDir = require(`os`).tmpdir() -const path = require(`path`) -const sharp = require(`./safe-sharp`) -const filenamify = require(`filenamify`) -const duotone = require(`./duotone`) -const { getPluginOptions, healOptions } = require(`./plugin-options`) -const { reportError } = require(`./report-error`) -const { - createContentDigest, -} = require(`gatsby-core-utils/create-content-digest`) - -exports.notMemoizedPrepareTraceSVGInputFile = async ({ - file, - options, - tmpFilePath, - reporter, -}) => { - let pipeline - try { - pipeline = sharp() - - if (!options.rotate) { - pipeline.rotate() - } - fs.createReadStream(file.absolutePath).pipe(pipeline) - } catch (err) { - reportError(`Failed to process image ${file.absolutePath}`, err, reporter) - return - } - - pipeline - .resize(options.width, options.height, { - position: options.cropFocus, - }) - .png({ - compressionLevel: options.pngCompressionLevel, - adaptiveFiltering: false, - force: options.toFormat === `png`, - }) - .jpeg({ - quality: options.quality, - progressive: options.jpegProgressive, - force: options.toFormat === `jpg`, - }) - - // grayscale - if (options.grayscale) { - pipeline = pipeline.grayscale() - } - - // rotate - if (options.rotate && options.rotate !== 0) { - pipeline = pipeline.rotate(options.rotate) - } - - // duotone - if (options.duotone) { - pipeline = await duotone(options.duotone, options.toFormat, pipeline) - } - - await new Promise((resolve, reject) => - pipeline.toFile(tmpFilePath, err => { - if (err) { - return reject(err) - } - return resolve() - }) - ) -} - -const optimize = svg => { - const { optimize } = require(`svgo`) - const { data } = optimize(svg, { - multipass: true, - floatPrecision: 0, - plugins: [ - { - name: `preset-default`, - params: { - overrides: { - // disable removeViewBox plugin - removeViewBox: false, - }, - }, - }, - { - name: `addAttributesToSVGElement`, - params: { - attributes: [ - { - preserveAspectRatio: `none`, - }, - ], - }, - }, - ], - }) - return data -} - -exports.notMemoizedtraceSVG = async ({ file, args, fileArgs, reporter }) => { - const options = healOptions( - getPluginOptions(), - { - // use maxWidth/maxHeight as width/height if available - // if width/height is used in fileArgs, the maxWidth/maxHeight - // values will be overritten - ...(fileArgs && fileArgs.maxWidth && fileArgs.maxHeight - ? { - height: fileArgs.maxHeight, - width: fileArgs.maxWidth, - } - : {}), - ...fileArgs, - }, - file.extension - ) - - const optionsHash = createContentDigest(options) - - const tmpFilePath = path.join( - tmpDir, - filenamify(`${file.internal.contentDigest}-${file.name}-${optionsHash}`) + - `.${file.extension}` - ) - - await exports.memoizedPrepareTraceSVGInputFile({ - tmpFilePath, - file, - options, - reporter, - }) - - const svgToMiniDataURI = require(`mini-svg-data-uri`) - const potrace = require(`@gatsbyjs/potrace`) - const trace = promisify(potrace.trace) - - const defaultArgs = { - color: `lightgray`, - optTolerance: 0.4, - turdSize: 100, - turnPolicy: potrace.Potrace.TURNPOLICY_MAJORITY, - } - - const optionsSVG = _.defaults({}, args, defaultArgs) - - // `srcset` attribute rejects URIs with literal spaces - const encodeSpaces = str => str.replace(/ /gi, `%20`) - - return trace(tmpFilePath, optionsSVG) - .then(optimize) - .then(svgToMiniDataURI) - .then(encodeSpaces) -} - -let memoizedPrepareTraceSVGInputFile -let memoizedTraceSVG -const createMemoizedFunctions = () => { - exports.memoizedPrepareTraceSVGInputFile = memoizedPrepareTraceSVGInputFile = - _.memoize( - exports.notMemoizedPrepareTraceSVGInputFile, - ({ tmpFilePath }) => tmpFilePath - ) - - exports.memoizedTraceSVG = memoizedTraceSVG = _.memoize( - exports.notMemoizedtraceSVG, - ({ file, args, fileArgs }) => - `${file.internal.contentDigest}${JSON.stringify(args)}${JSON.stringify( - fileArgs - )}` - ) -} - -// This is very hacky, but memoized function are pretty tricky to spy on -// in tests ;( -createMemoizedFunctions() -exports.createMemoizedFunctions = () => { - createMemoizedFunctions() -} - -exports.clearMemoizeCaches = () => { - memoizedTraceSVG.cache.clear() - memoizedPrepareTraceSVGInputFile.cache.clear() -} diff --git a/packages/gatsby-plugin-utils/package.json b/packages/gatsby-plugin-utils/package.json index 6e7612a80f6ee..9645d0650ab66 100644 --- a/packages/gatsby-plugin-utils/package.json +++ b/packages/gatsby-plugin-utils/package.json @@ -47,7 +47,6 @@ "homepage": "https://github.com/gatsbyjs/gatsby/tree/master/packages/gatsby-plugin-utils#readme", "dependencies": { "@babel/runtime": "^7.15.4", - "@gatsbyjs/potrace": "^2.3.0", "fastq": "^1.13.0", "fs-extra": "^10.1.0", "gatsby-core-utils": "^4.2.0-next.0", @@ -55,9 +54,7 @@ "graphql-compose": "^9.0.9", "import-from": "^4.0.0", "joi": "^17.4.2", - "mime": "^3.0.0", - "mini-svg-data-uri": "^1.4.4", - "svgo": "^2.8.0" + "mime": "^3.0.0" }, "devDependencies": { "@babel/cli": "^7.15.4", diff --git a/packages/gatsby-plugin-utils/src/polyfill-remote-file/__tests__/gatsby-image-resolver.ts b/packages/gatsby-plugin-utils/src/polyfill-remote-file/__tests__/gatsby-image-resolver.ts index 2cbcca6ad0ffb..5c065601a1067 100644 --- a/packages/gatsby-plugin-utils/src/polyfill-remote-file/__tests__/gatsby-image-resolver.ts +++ b/packages/gatsby-plugin-utils/src/polyfill-remote-file/__tests__/gatsby-image-resolver.ts @@ -724,7 +724,7 @@ describe(`gatsbyImageData`, () => { `) }) - it(`should generate tracedSVG placeholder`, async () => { + it(`should generate tracedSVG placeholder (fallback to dominant_color)`, async () => { fetchRemoteFile.mockResolvedValueOnce( path.join(__dirname, `__fixtures__`, `dog-portrait.jpg`) ) @@ -745,11 +745,11 @@ describe(`gatsbyImageData`, () => { cacheKey: `1`, directory: expect.stringContaining(cacheDir), }) - expect(fixedResult?.placeholder).toMatchInlineSnapshot(` - Object { - "fallback": "data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20width='20'%20height='33'%20viewBox='0%200%2020%2033'%3e%3cpath%20d='M6%201C4%205%204%205%203%203L2%201C0%201%200%205%200%2014s0%209%203%208c4%200%204%200%204-2v-8H6c0-1%202-3%204-3s2%200%202-2l-1-4c0-3%200-3-3-3L6%201'%20fill='%23d3d3d3'%20fill-rule='evenodd'/%3e%3c/svg%3e", - } - `) + // placeholder doesn't exist, instead backgroundColor is used + expect(fixedResult?.placeholder).not.toBeDefined() + expect(fixedResult?.backgroundColor).toMatchInlineSnapshot( + `"rgb(56,40,40)"` + ) }) it(`should render avif, webp other format in this order`, async () => { diff --git a/packages/gatsby-plugin-utils/src/polyfill-remote-file/graphql/gatsby-image-resolver.ts b/packages/gatsby-plugin-utils/src/polyfill-remote-file/graphql/gatsby-image-resolver.ts index 495b29005e963..4befd828b67cf 100644 --- a/packages/gatsby-plugin-utils/src/polyfill-remote-file/graphql/gatsby-image-resolver.ts +++ b/packages/gatsby-plugin-utils/src/polyfill-remote-file/graphql/gatsby-image-resolver.ts @@ -76,6 +76,7 @@ const GATSBY_SHOULD_TRACK_IMAGE_CDN_URLS = [`true`, `1`].includes( process.env.GATSBY_SHOULD_TRACK_IMAGE_CDN_URLS || `` ) +let didShowTraceSVGRemovalWarning = false export async function gatsbyImageResolver( source: IRemoteFileNode, args: IGatsbyImageDataArgs, @@ -121,6 +122,14 @@ export async function gatsbyImageResolver( if (!args.placeholder) { args.placeholder = PlaceholderType.DOMINANT_COLOR + } else if (args.placeholder === PlaceholderType.TRACED_SVG) { + if (!didShowTraceSVGRemovalWarning) { + console.warn( + `"TRACED_SVG" placeholder argument value is no longer supported (used in gatsbyImage processing), falling back to "DOMINANT_COLOR". See https://gatsby.dev/tracesvg-removal/` + ) + didShowTraceSVGRemovalWarning = true + } + args.placeholder = PlaceholderType.DOMINANT_COLOR } if (!args.quality) { @@ -305,9 +314,9 @@ export function generateGatsbyImageFieldConfig( defaultValue: enums.placeholder.getField(`DOMINANT_COLOR`).value, description: stripIndent` Format of generated placeholder image, displayed while the main image loads. - BLURRED: a blurred, low resolution image, encoded as a base64 data URI (default) - DOMINANT_COLOR: a solid color, calculated from the dominant color of the image. - TRACED_SVG: a low-resolution traced SVG of the image. + BLURRED: a blurred, low resolution image, encoded as a base64 data URI + DOMINANT_COLOR: a solid color, calculated from the dominant color of the image (default). + TRACED_SVG: deprecated. Will use DOMINANT_COLOR. NONE: no placeholder. Set the argument "backgroundColor" to use a fixed background color.`, }, aspectRatio: { diff --git a/packages/gatsby-plugin-utils/src/polyfill-remote-file/placeholder-handler.ts b/packages/gatsby-plugin-utils/src/polyfill-remote-file/placeholder-handler.ts index f1ac0303349fd..c97cdd2863604 100644 --- a/packages/gatsby-plugin-utils/src/polyfill-remote-file/placeholder-handler.ts +++ b/packages/gatsby-plugin-utils/src/polyfill-remote-file/placeholder-handler.ts @@ -34,6 +34,7 @@ const PLACEHOLDER_TRACED_WIDTH = 200 let tmpDir: string +let didShowTraceSVGRemovalWarning = false const queue = Queue< undefined, { @@ -66,6 +67,19 @@ const queue = Queue< httpHeaders, }) + if (type === PlaceholderType.TRACED_SVG) { + if (!didShowTraceSVGRemovalWarning) { + // we should not hit this code path, field resolver should fallback earlier, this is just in-case. + // also this falls back to BLURRED because the shape is compatible. DOMINANT_COLOR is not compatible + // and fallback to DOMINANT_COLOR need to happen very early on and not when already generating value + console.warn( + `"TRACED_SVG" placeholder is no longer supported, falling back to "BLURRED". See https://gatsby.dev/tracesvg-removal/` + ) + didShowTraceSVGRemovalWarning = true + } + type = PlaceholderType.BLURRED + } + switch (type) { case PlaceholderType.BLURRED: { let buffer: Buffer @@ -99,76 +113,6 @@ const queue = Queue< : `rgba(0,0,0,0)` ) } - case PlaceholderType.TRACED_SVG: { - let buffer: Buffer - - try { - const fileStream = createReadStream(filePath) - const pipeline = sharp() - fileStream.pipe(pipeline) - buffer = await pipeline - .resize( - PLACEHOLDER_BASE64_WIDTH, - Math.ceil(PLACEHOLDER_BASE64_WIDTH / (width / height)) - ) - .toBuffer() - } catch (err) { - buffer = await readFile(filePath) - } - - const [{ trace, Potrace }, { optimize }, { default: svgToMiniDataURI }] = - await Promise.all([ - import(`@gatsbyjs/potrace`), - import(`svgo`), - import(`mini-svg-data-uri`), - ]) - - trace( - buffer, - { - color: `lightgray`, - optTolerance: 0.4, - turdSize: 100, - turnPolicy: Potrace.TURNPOLICY_MAJORITY, - }, - async (err, svg) => { - if (err) { - return cb(err) - } - - try { - const { data } = await optimize(svg, { - multipass: true, - floatPrecision: 0, - plugins: [ - { - name: `preset-default`, - params: { - overrides: { - // customize default plugin options - removeViewBox: false, - - // or disable plugins - addAttributesToSVGElement: { - attributes: [ - { - preserveAspectRatio: `none`, - }, - ], - }, - }, - }, - }, - ], - }) - - return cb(null, svgToMiniDataURI(data).replace(/ /gi, `%20`)) - } catch (err) { - return cb(err) - } - } - ) - } } }, QUEUE_CONCURRENCY) diff --git a/packages/gatsby-remark-images/package.json b/packages/gatsby-remark-images/package.json index 12830d574d837..59c8fa1b996fd 100644 --- a/packages/gatsby-remark-images/package.json +++ b/packages/gatsby-remark-images/package.json @@ -8,7 +8,6 @@ }, "dependencies": { "@babel/runtime": "^7.15.4", - "@gatsbyjs/potrace": "^2.3.0", "chalk": "^4.1.2", "cheerio": "^1.0.0-rc.10", "gatsby-core-utils": "^4.2.0-next.0", diff --git a/packages/gatsby-remark-images/src/__tests__/__snapshots__/index.js.snap b/packages/gatsby-remark-images/src/__tests__/__snapshots__/index.js.snap index 138fbf30a3090..af0871b2661c1 100644 --- a/packages/gatsby-remark-images/src/__tests__/__snapshots__/index.js.snap +++ b/packages/gatsby-remark-images/src/__tests__/__snapshots__/index.js.snap @@ -124,6 +124,37 @@ exports[`disableBgImageOnAlpha does not disable background image on transparent " `; +exports[`it doesn't use tracedSVG placeholder (deprecated and fallback to base64) 1`] = ` +" + + + \\"image\\" + + " +`; + exports[`it handles goofy nesting properly 1`] = ` "" `; -exports[`it uses tracedSVG placeholder when enabled 1`] = ` -" - - - \\"image\\" - - " -`; - exports[`markdownCaptions display title in markdown as caption when showCaptions === true && markdownCaptions === true 1`] = ` "
{ + // silence warnings +}) + +beforeEach(() => { + warnSpy.mockClear() +}) describe(`pluginOptionsSchema`, () => { it(`should provide meaningful errors when fields are invalid`, async () => { @@ -70,6 +77,10 @@ describe(`pluginOptionsSchema`, () => { ) expect(isValid).toBe(true) + + expect(warnSpy).toBeCalledWith( + `"tracedSVG" plugin option for "gatsby-remark-images" is no longer supported. Blurred placeholder will be used. See https://gatsby.dev/tracesvg-removal/` + ) expect(errors).toEqual([]) }) @@ -161,6 +172,12 @@ describe(`pluginOptionsSchema`, () => { ) expect(isValid).toBe(true) + + if (booleanValue) { + expect(warnSpy).toBeCalledWith( + `"tracedSVG" plugin option for "gatsby-remark-images" is no longer supported. Blurred placeholder will be used. See https://gatsby.dev/tracesvg-removal/` + ) + } expect(errors).toEqual([]) }) }) @@ -171,7 +188,7 @@ describe(`pluginOptionsSchema`, () => { pluginOptionsSchema, { tracedSVG: { - turnPolicy: Potrace.TURNPOLICY_RIGHT, + turnPolicy: `TURNPOLICY_RIGHT`, turdSize: 50, alphaMax: 0.5, optCurve: false, @@ -185,6 +202,10 @@ describe(`pluginOptionsSchema`, () => { ) expect(isValid).toBe(true) + + expect(warnSpy).toBeCalledWith( + `"tracedSVG" plugin option for "gatsby-remark-images" is no longer supported. Blurred placeholder will be used. See https://gatsby.dev/tracesvg-removal/` + ) expect(errors).toEqual([]) }) @@ -193,7 +214,7 @@ describe(`pluginOptionsSchema`, () => { pluginOptionsSchema, { tracedSVG: { - turnPolicy: Potrace.TURNPOLICY_RIGHT, + turnPolicy: `TURNPOLICY_RIGHT`, turdSize: 50, // alphaMax: 0.5, // optCurve: 0.2, @@ -207,6 +228,10 @@ describe(`pluginOptionsSchema`, () => { ) expect(isValid).toBe(true) + + expect(warnSpy).toBeCalledWith( + `"tracedSVG" plugin option for "gatsby-remark-images" is no longer supported. Blurred placeholder will be used. See https://gatsby.dev/tracesvg-removal/` + ) expect(errors).toEqual([]) }) @@ -243,16 +268,20 @@ describe(`pluginOptionsSchema`, () => { ) expect(isValid).toBe(true) + + expect(warnSpy).toBeCalledWith( + `"tracedSVG" plugin option for "gatsby-remark-images" is no longer supported. Blurred placeholder will be used. See https://gatsby.dev/tracesvg-removal/` + ) expect(errors).toEqual([]) }) it.each([ - Potrace.TURNPOLICY_BLACK, - Potrace.TURNPOLICY_WHITE, - Potrace.TURNPOLICY_LEFT, - Potrace.TURNPOLICY_RIGHT, - Potrace.TURNPOLICY_MINORITY, - Potrace.TURNPOLICY_MAJORITY, + `black`, + `white`, + `left`, + `TURNPOLICY_RIGHT`, + `minority`, + `majority`, ])(`supports setting by policy value (%s)`, async value => { const { isValid, errors } = await testPluginOptionsSchema( pluginOptionsSchema, @@ -262,6 +291,10 @@ describe(`pluginOptionsSchema`, () => { ) expect(isValid).toBe(true) + + expect(warnSpy).toBeCalledWith( + `"tracedSVG" plugin option for "gatsby-remark-images" is no longer supported. Blurred placeholder will be used. See https://gatsby.dev/tracesvg-removal/` + ) expect(errors).toEqual([]) }) @@ -288,7 +321,7 @@ describe(`pluginOptionsSchema`, () => { [ `THRESHOLD_AUTO`, { - value: Potrace.THRESHOLD_AUTO, + value: -1, expectedIsValid: true, }, ], @@ -324,6 +357,10 @@ describe(`pluginOptionsSchema`, () => { ) expect(isValid).toBe(true) + + expect(warnSpy).toBeCalledWith( + `"tracedSVG" plugin option for "gatsby-remark-images" is no longer supported. Blurred placeholder will be used. See https://gatsby.dev/tracesvg-removal/` + ) expect(errors).toEqual([]) }) diff --git a/packages/gatsby-remark-images/src/__tests__/index.js b/packages/gatsby-remark-images/src/__tests__/index.js index 56c44c095e01e..90cae91aeebcf 100644 --- a/packages/gatsby-remark-images/src/__tests__/index.js +++ b/packages/gatsby-remark-images/src/__tests__/index.js @@ -25,7 +25,6 @@ jest.mock(`gatsby-plugin-sharp`, () => { }) const Remark = require(`remark`) -const { Potrace } = require(`@gatsbyjs/potrace`) const queryString = require(`query-string`) const cheerio = require(`cheerio`) const toHAST = require(`mdast-util-to-hast`) @@ -453,7 +452,7 @@ test(`it transforms images in markdown with query strings`, async () => { expect(node.value).not.toMatch(``) }) -test(`it uses tracedSVG placeholder when enabled`, async () => { +test(`it doesn't use tracedSVG placeholder (deprecated and fallback to base64)`, async () => { const imagePath = `images/my-image.jpeg` const content = ` ![image](./${imagePath}) @@ -469,16 +468,7 @@ test(`it uses tracedSVG placeholder when enabled`, async () => { expect(node.type).toBe(`html`) expect(node.value).toMatchSnapshot() expect(node.value).not.toMatch(``) - expect(mockTraceSVG).toBeCalledTimes(1) - - expect(mockTraceSVG).toBeCalledWith( - expect.objectContaining({ - // fileArgs cannot be left undefined or traceSVG errors - fileArgs: expect.any(Object), - // args containing Potrace constants should be translated to their values - args: { color: Potrace.COLOR_AUTO, turnPolicy: Potrace.TURNPOLICY_LEFT }, - }) - ) + expect(mockTraceSVG).toBeCalledTimes(0) }) describe(`showCaptions`, () => { diff --git a/packages/gatsby-remark-images/src/gatsby-node.js b/packages/gatsby-remark-images/src/gatsby-node.js index 25f9f2f6de9a5..c1157c6e4d574 100644 --- a/packages/gatsby-remark-images/src/gatsby-node.js +++ b/packages/gatsby-remark-images/src/gatsby-node.js @@ -1,5 +1,3 @@ -const { Potrace } = require(`@gatsbyjs/potrace`) - exports.pluginOptionsSchema = function ({ Joi }) { return Joi.object({ maxWidth: Joi.number() @@ -69,30 +67,34 @@ exports.pluginOptionsSchema = function ({ Joi }) { `TURNPOLICY_MINORITY`, `TURNPOLICY_MAJORITY`, // it also allow using actual policy values - Potrace.TURNPOLICY_BLACK, - Potrace.TURNPOLICY_WHITE, - Potrace.TURNPOLICY_LEFT, - Potrace.TURNPOLICY_RIGHT, - Potrace.TURNPOLICY_MINORITY, - Potrace.TURNPOLICY_MAJORITY + `black`, + `white`, + `left`, + `right`, + `minority`, + `majority` ) - .default(Potrace.TURNPOLICY_MAJORITY), + .default(`majority`), turdSize: Joi.number().default(100), alphaMax: Joi.number(), optCurve: Joi.boolean().default(true), optTolerance: Joi.number().default(0.4), threshold: Joi.alternatives() - .try( - Joi.number().min(0).max(255), - Joi.number().valid(Potrace.THRESHOLD_AUTO) - ) - .default(Potrace.THRESHOLD_AUTO), + .try(Joi.number().min(0).max(255), Joi.number().valid(-1)) + .default(-1), blackOnWhite: Joi.boolean().default(true), color: Joi.string().default(`lightgray`), background: Joi.string().default(`transparent`), }) ) - .default(false) + .custom(value => { + if (!!value && !process.env.GATSBY_WORKER_ID) { + console.warn( + `"tracedSVG" plugin option for "gatsby-remark-images" is no longer supported. Blurred placeholder will be used. See https://gatsby.dev/tracesvg-removal/` + ) + } + return undefined + }) .description( `Use traced SVGs for placeholder images instead of the “blur up” effect. Pass true for traced SVGs with the default settings (seen here), or an object of options to override the default. For example, pass { color: "#F00", turnPolicy: "TURNPOLICY_MAJORITY" } to change the color of the trace to red and the turn policy to TURNPOLICY_MAJORITY. See node-potrace parameter documentation for a full listing and explanation of the available options.` ), diff --git a/packages/gatsby-remark-images/src/index.js b/packages/gatsby-remark-images/src/index.js index 0f07da46b026f..12009f0c13e0e 100644 --- a/packages/gatsby-remark-images/src/index.js +++ b/packages/gatsby-remark-images/src/index.js @@ -347,32 +347,7 @@ module.exports = ( `.trim() } - let placeholderImageData = fluidResult.base64 - - // if options.tracedSVG is enabled generate the traced SVG and use that as the placeholder image - if (options.tracedSVG) { - let args = typeof options.tracedSVG === `object` ? options.tracedSVG : {} - - // Translate Potrace constants (e.g. TURNPOLICY_LEFT, COLOR_AUTO) to the values Potrace expects - const { Potrace } = require(`@gatsbyjs/potrace`) - const argsKeys = Object.keys(args) - args = argsKeys.reduce((result, key) => { - const value = args[key] - result[key] = Potrace.hasOwnProperty(value) ? Potrace[value] : value - return result - }, {}) - - const tracedSVG = await traceSVG({ - file: imageNode, - args, - fileArgs: args, - cache, - reporter, - }) - - // Escape single quotes so the SVG data can be used in inline style attribute with single quotes - placeholderImageData = tracedSVG.replace(/'/g, `\\'`) - } + const placeholderImageData = fluidResult.base64 const ratio = `${(1 / fluidResult.aspectRatio) * 100}%` diff --git a/packages/gatsby-source-contentful/src/__tests__/__snapshots__/gatsby-plugin-image.js.snap b/packages/gatsby-source-contentful/src/__tests__/__snapshots__/gatsby-plugin-image.js.snap index 4a949fcad7511..1e57c49704a85 100644 --- a/packages/gatsby-source-contentful/src/__tests__/__snapshots__/gatsby-plugin-image.js.snap +++ b/packages/gatsby-source-contentful/src/__tests__/__snapshots__/gatsby-plugin-image.js.snap @@ -57,9 +57,9 @@ https://images.ctfassets.net:443/k8iqpp6u0ior/3ljGfnpegOnBTFGhV07iC1/94257340bda } `; -exports[`gatsby-plugin-image defaults via gatsby-plugin-sharp custom placeholder tracedSVG 1`] = ` +exports[`gatsby-plugin-image defaults via gatsby-plugin-sharp custom placeholder tracedSVG (falls back to DOMINANT_COLOR) 1`] = ` Object { - "backgroundColor": undefined, + "backgroundColor": "#080808", "height": 338, "images": Object { "fallback": Object { @@ -80,9 +80,6 @@ https://images.ctfassets.net:443/k8iqpp6u0ior/3ljGfnpegOnBTFGhV07iC1/94257340bda ], }, "layout": "constrained", - "placeholder": Object { - "fallback": "data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20width='400'%20height='82'%20viewBox='0%200%20400%2082'%20preserveAspectRatio='none'%3e%3cpath%20d='M27%203C15%208%2010%2013%2010%2019c0%208%209%2011%2015%205%2010-8%2022-8%2032%200%204%204%209%204%2012%201C82%2012%2048-6%2027%203m305%207c-5%203-6%208-6%2029s0%2022%205%2022%205-1%205-16V32h4c4%200%206-2%206-5-1-3-2-3-6-3s-4%200-4-2c0-3%203-6%206-6%204%200%205-2%205-5-1-4-10-4-15-1m-130%205-1%2019v19l3%203c3%203%203%203%209%203l6%201v-4c0-3-1-3-4-4-4-1-5-4-5-13%200-7%201-7%203-7%206%201%209-3%207-7-1-2-2-2-5-2h-5v-4c0-4-1-5-4-5l-4%201m102%200c-2%202-1%2034%200%2038%203%206%207%208%2015%207%205%200%203-8-1-8s-5-2-5-11v-9h4c4%200%206-1%206-4%200-4-2-5-6-5s-4%200-4-3l-1-5h-8m87%200v45l4%201%204-1V15h-8M96%2024c-14%207-15%2027-2%2034%207%204%2019%203%2023-2%201-2%201-2-1-5-3-2-3-2-7-1-10%204-17-3-14-12%202-6%208-9%2014-6%203%201%208-2%208-5%200-4-16-7-21-3m32%200c-15%208-12%2032%204%2036%2015%204%2028-9%2024-24-3-11-17-17-28-12m49-1-5%202c-3%202-3%202-4%200s-2-2-4-2l-3%201-1%2015c-1%2019-1%2021%205%2021h4V49c1-13%201-14%205-17%204-2%206-1%2010%201%202%203%202%203%203%2015l1%2012h8V46c0-15-1-17-7-21-3-2-9-3-12-2m55%201c-15%207-15%2029%200%2035%207%203%2018%202%2023-2l2-2-2-3c-3-2-3-2-6-1-6%202-13%201-16-1-4-5-3-5%2012-5h14v-5c0-14-14-22-27-16m31%200-1%2018v17l2%201c6%202%208%200%208-12%200-13%202-16%2010-16%205%200%207%203%207%2016l1%2011c2%202%205%202%207%201%203-2%203-25-1-30-4-7-14-9-21-5-4%202-4%202-4%200-1-2-6-3-8-1m87%200c-3%202-2%2024%200%2029%204%208%2014%2010%2022%206l4-1c0%202%205%204%207%202%201-1%202-3%202-18V25l-4-1c-6%200-6%201-6%2013s-2%2015-9%2015-8-3-8-16c0-10%200-11-2-12h-6M131%2034c-6%205-3%2016%204%2018%209%201%2015-8%2010-16-3-6-9-7-14-2M14%2055c-9%205-5%2015%208%2022%2016%208%2034%205%2046-6%2010-9-1-22-11-13-10%208-22%208-31%200-4-4-9-5-12-3'%20fill='%23639'%20fill-rule='evenodd'/%3e%3c/svg%3e", - }, "width": "1646", } `; @@ -333,9 +330,9 @@ https://images.ctfassets.net:443/k8iqpp6u0ior/3ljGfnpegOnBTFGhV07iC1/94257340bda } `; -exports[`gatsby-plugin-image query arguments placeholder traced svg 1`] = ` +exports[`gatsby-plugin-image query arguments placeholder traced svg (falls back to DOMINANT_COLOR) 1`] = ` Object { - "backgroundColor": undefined, + "backgroundColor": "#080808", "height": 338, "images": Object { "fallback": Object { @@ -356,9 +353,6 @@ https://images.ctfassets.net:443/k8iqpp6u0ior/3ljGfnpegOnBTFGhV07iC1/94257340bda ], }, "layout": "constrained", - "placeholder": Object { - "fallback": "data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20width='400'%20height='82'%20viewBox='0%200%20400%2082'%20preserveAspectRatio='none'%3e%3cpath%20d='M27%203C15%208%2010%2013%2010%2019c0%208%209%2011%2015%205%2010-8%2022-8%2032%200%204%204%209%204%2012%201C82%2012%2048-6%2027%203m305%207c-5%203-6%208-6%2029s0%2022%205%2022%205-1%205-16V32h4c4%200%206-2%206-5-1-3-2-3-6-3s-4%200-4-2c0-3%203-6%206-6%204%200%205-2%205-5-1-4-10-4-15-1m-130%205-1%2019v19l3%203c3%203%203%203%209%203l6%201v-4c0-3-1-3-4-4-4-1-5-4-5-13%200-7%201-7%203-7%206%201%209-3%207-7-1-2-2-2-5-2h-5v-4c0-4-1-5-4-5l-4%201m102%200c-2%202-1%2034%200%2038%203%206%207%208%2015%207%205%200%203-8-1-8s-5-2-5-11v-9h4c4%200%206-1%206-4%200-4-2-5-6-5s-4%200-4-3l-1-5h-8m87%200v45l4%201%204-1V15h-8M96%2024c-14%207-15%2027-2%2034%207%204%2019%203%2023-2%201-2%201-2-1-5-3-2-3-2-7-1-10%204-17-3-14-12%202-6%208-9%2014-6%203%201%208-2%208-5%200-4-16-7-21-3m32%200c-15%208-12%2032%204%2036%2015%204%2028-9%2024-24-3-11-17-17-28-12m49-1-5%202c-3%202-3%202-4%200s-2-2-4-2l-3%201-1%2015c-1%2019-1%2021%205%2021h4V49c1-13%201-14%205-17%204-2%206-1%2010%201%202%203%202%203%203%2015l1%2012h8V46c0-15-1-17-7-21-3-2-9-3-12-2m55%201c-15%207-15%2029%200%2035%207%203%2018%202%2023-2l2-2-2-3c-3-2-3-2-6-1-6%202-13%201-16-1-4-5-3-5%2012-5h14v-5c0-14-14-22-27-16m31%200-1%2018v17l2%201c6%202%208%200%208-12%200-13%202-16%2010-16%205%200%207%203%207%2016l1%2011c2%202%205%202%207%201%203-2%203-25-1-30-4-7-14-9-21-5-4%202-4%202-4%200-1-2-6-3-8-1m87%200c-3%202-2%2024%200%2029%204%208%2014%2010%2022%206l4-1c0%202%205%204%207%202%201-1%202-3%202-18V25l-4-1c-6%200-6%201-6%2013s-2%2015-9%2015-8-3-8-16c0-10%200-11-2-12h-6M131%2034c-6%205-3%2016%204%2018%209%201%2015-8%2010-16-3-6-9-7-14-2M14%2055c-9%205-5%2015%208%2022%2016%208%2034%205%2046-6%2010-9-1-22-11-13-10%208-22%208-31%200-4-4-9-5-12-3'%20fill='%23d3d3d3'%20fill-rule='evenodd'/%3e%3c/svg%3e", - }, "width": "1646", } `; diff --git a/packages/gatsby-source-contentful/src/__tests__/gatsby-plugin-image.js b/packages/gatsby-source-contentful/src/__tests__/gatsby-plugin-image.js index b60b7139e628d..033ea67336beb 100644 --- a/packages/gatsby-source-contentful/src/__tests__/gatsby-plugin-image.js +++ b/packages/gatsby-source-contentful/src/__tests__/gatsby-plugin-image.js @@ -242,7 +242,7 @@ describe(`gatsby-plugin-image`, () => { expect(resp.placeholder.fallback).toMatch(/^data:image\/png;base64,.+/) expect(resp).toMatchSnapshot() }) - it(`placeholder traced svg`, async () => { + it(`placeholder traced svg (falls back to DOMINANT_COLOR)`, async () => { const resp = await extendedNodeType.gatsbyImageData.resolve( exampleImage, // @ts-ignore @@ -252,9 +252,8 @@ describe(`gatsby-plugin-image`, () => { null, null ) - expect(resp.backgroundColor).toEqual(undefined) - expect(resp.placeholder.fallback).toMatch(/^data:image\/svg\+xml,.+/) - expect(resp.placeholder.fallback).toContain(`fill='%23d3d3d3'`) + expect(resp.backgroundColor).toEqual(`#080808`) + expect(resp.placeholder).not.toBeDefined() expect(resp).toMatchSnapshot() }) }) @@ -302,7 +301,7 @@ describe(`gatsby-plugin-image`, () => { expect(resp).toMatchSnapshot() }) - it(`custom placeholder tracedSVG`, async () => { + it(`custom placeholder tracedSVG (falls back to DOMINANT_COLOR)`, async () => { setPluginOptions({ defaults: { placeholder: `tracedSVG`, @@ -319,8 +318,8 @@ describe(`gatsby-plugin-image`, () => { null, null ) - expect(resp.placeholder.fallback).toMatch(/^data:image\/svg\+xml,.+/) - expect(resp.placeholder.fallback).toContain(`fill='%23639'`) + expect(resp.backgroundColor).toEqual(`#080808`) + expect(resp.placeholder).not.toBeDefined() expect(resp).toMatchSnapshot() }) diff --git a/packages/gatsby-source-contentful/src/gatsby-plugin-image.js b/packages/gatsby-source-contentful/src/gatsby-plugin-image.js index d642aff4ffc7a..963df00d8b013 100644 --- a/packages/gatsby-source-contentful/src/gatsby-plugin-image.js +++ b/packages/gatsby-source-contentful/src/gatsby-plugin-image.js @@ -252,6 +252,7 @@ export function generateImageSource( return { width, height, format: toFormat, src } } +let didShowTraceSVGRemovalWarning = false export async function resolveGatsbyImageData( image, options, @@ -286,6 +287,16 @@ export async function resolveGatsbyImageData( options = doMergeDefaults(options, defaults) + if (options.placeholder === `tracedSVG`) { + if (!didShowTraceSVGRemovalWarning) { + console.warn( + `"TRACED_SVG" placeholder argument value is no longer supported (used in ContentfulAsset.gatsbyImageData processing), falling back to "DOMINANT_COLOR". See https://gatsby.dev/tracesvg-removal/` + ) + didShowTraceSVGRemovalWarning = true + } + options.placeholder = `dominantColor` + } + const { baseUrl, contentType, width, height } = getBasicImageProps( image, options @@ -336,11 +347,7 @@ export async function resolveGatsbyImageData( } if (options.placeholder === `tracedSVG`) { - placeholderDataURI = await getTracedSVG({ - image, - options, - cache, - }) + console.error(`this shouldn't happen`) } if (placeholderDataURI) { diff --git a/packages/gatsby-source-shopify/__tests__/__snapshots__/create-resolvers.ts.snap b/packages/gatsby-source-shopify/__tests__/__snapshots__/create-resolvers.ts.snap index bf094215a8117..66a9f7a38d9b6 100644 --- a/packages/gatsby-source-shopify/__tests__/__snapshots__/create-resolvers.ts.snap +++ b/packages/gatsby-source-shopify/__tests__/__snapshots__/create-resolvers.ts.snap @@ -106,9 +106,9 @@ Default is [ 1, 2 ] for fixed images, meaning 1x, 2x, 3x, and [0.25, 0.5, 1, 2] }, "placeholder": Object { "description": "Format of generated placeholder image, displayed while the main image loads. -BLURRED: a blurred, low resolution image, encoded as a base64 data URI (default) -DOMINANT_COLOR: a solid color, calculated from the dominant color of the image. -TRACED_SVG: a low-resolution traced SVG of the image. +BLURRED: a blurred, low resolution image, encoded as a base64 data URI. +DOMINANT_COLOR: a solid color, calculated from the dominant color of the image (default). +TRACED_SVG: deprecated. Will use DOMINANT_COLOR. NONE: no placeholder. Set the argument \\"backgroundColor\\" to use a fixed background color.", "type": "GatsbyImagePlaceholder", }, @@ -212,9 +212,9 @@ Default is [ 1, 2 ] for fixed images, meaning 1x, 2x, 3x, and [0.25, 0.5, 1, 2] }, "placeholder": Object { "description": "Format of generated placeholder image, displayed while the main image loads. -BLURRED: a blurred, low resolution image, encoded as a base64 data URI (default) -DOMINANT_COLOR: a solid color, calculated from the dominant color of the image. -TRACED_SVG: a low-resolution traced SVG of the image. +BLURRED: a blurred, low resolution image, encoded as a base64 data URI. +DOMINANT_COLOR: a solid color, calculated from the dominant color of the image (default). +TRACED_SVG: deprecated. Will use DOMINANT_COLOR. NONE: no placeholder. Set the argument \\"backgroundColor\\" to use a fixed background color.", "type": "GatsbyImagePlaceholder", }, diff --git a/packages/gatsby-transformer-sharp/package.json b/packages/gatsby-transformer-sharp/package.json index b37b040158628..a135363d3c0a5 100644 --- a/packages/gatsby-transformer-sharp/package.json +++ b/packages/gatsby-transformer-sharp/package.json @@ -8,7 +8,6 @@ }, "dependencies": { "@babel/runtime": "^7.15.4", - "@gatsbyjs/potrace": "^2.3.0", "bluebird": "^3.7.2", "common-tags": "^1.8.2", "fs-extra": "^10.1.0", diff --git a/packages/gatsby-transformer-sharp/src/customize-schema.js b/packages/gatsby-transformer-sharp/src/customize-schema.js index 88fdb4ef1e199..f2b09e6e1e58f 100644 --- a/packages/gatsby-transformer-sharp/src/customize-schema.js +++ b/packages/gatsby-transformer-sharp/src/customize-schema.js @@ -14,7 +14,6 @@ const { base64, fluid, fixed, - traceSVG, generateImageData, } = require(`gatsby-plugin-sharp`) const { hasFeature } = require(`gatsby-plugin-utils`) @@ -67,15 +66,7 @@ function toArray(buf) { return arr } -const getTracedSVG = async ({ file, image, fieldArgs, cache, reporter }) => - traceSVG({ - file, - args: { ...fieldArgs.traceSVG }, - fileArgs: fieldArgs, - cache, - reporter, - }) - +let didShowTraceSVGRemovalWarningFixed = false const fixedNodeType = ({ pathPrefix, getNodeAndSavePathDependency, @@ -90,12 +81,15 @@ const fixedNodeType = ({ base64: { type: GraphQLString }, tracedSVG: { type: GraphQLString, - resolve: parent => - getTracedSVG({ - ...parent, - cache, - reporter, - }), + resolve: parent => { + if (!didShowTraceSVGRemovalWarningFixed) { + console.warn( + `"tracedSVG" placeholder field is no longer supported (used in ImageSharp.fixed processing), falling back to "base64". See https://gatsby.dev/tracesvg-removal/` + ) + didShowTraceSVGRemovalWarningFixed = true + } + return parent.base64 + }, }, aspectRatio: { type: GraphQLFloat }, width: { type: new GraphQLNonNull(GraphQLFloat) }, @@ -234,6 +228,7 @@ const fixedNodeType = ({ } } +let didShowTraceSVGRemovalWarningFluid = false const fluidNodeType = ({ pathPrefix, getNodeAndSavePathDependency, @@ -248,12 +243,15 @@ const fluidNodeType = ({ base64: { type: GraphQLString }, tracedSVG: { type: GraphQLString, - resolve: parent => - getTracedSVG({ - ...parent, - cache, - reporter, - }), + resolve: parent => { + if (!didShowTraceSVGRemovalWarningFluid) { + console.warn( + `"tracedSVG" placeholder field is no longer supported (used in ImageSharp.fluid processing), falling back to "base64". See https://gatsby.dev/tracesvg-removal/` + ) + didShowTraceSVGRemovalWarningFluid = true + } + return parent.base64 + }, }, aspectRatio: { type: new GraphQLNonNull(GraphQLFloat) }, src: { type: new GraphQLNonNull(GraphQLString) }, @@ -401,6 +399,7 @@ const fluidNodeType = ({ } } +let didShowTraceSVGRemovalWarningGatsbyImageData = false const imageNodeType = ({ pathPrefix, getNodeAndSavePathDependency, @@ -447,9 +446,9 @@ const imageNodeType = ({ type: ImagePlaceholderType, description: stripIndent` Format of generated placeholder image, displayed while the main image loads. - BLURRED: a blurred, low resolution image, encoded as a base64 data URI (default) - DOMINANT_COLOR: a solid color, calculated from the dominant color of the image. - TRACED_SVG: a low-resolution traced SVG of the image. + BLURRED: a blurred, low resolution image, encoded as a base64 data URI + DOMINANT_COLOR: a solid color, calculated from the dominant color of the image (default). + TRACED_SVG: deprecated. Will use DOMINANT_COLOR. NONE: no placeholder. Set "background" to use a fixed background color.`, }, blurredOptions: { @@ -529,6 +528,17 @@ const imageNodeType = ({ reporter.warn(`Please upgrade gatsby-plugin-sharp`) return null } + + if (fieldArgs?.placeholder === `tracedSVG`) { + if (!didShowTraceSVGRemovalWarningGatsbyImageData) { + console.warn( + `"TRACED_SVG" placeholder argument value is no longer supported (used in ImageSharp.gatsbyImageData processing), falling back to "DOMINANT_COLOR". See https://gatsby.dev/tracesvg-removal/` + ) + didShowTraceSVGRemovalWarningGatsbyImageData = true + } + fieldArgs.placeholder = `dominantColor` + } + const imageData = await generateImageData({ file, args: fieldArgs, @@ -548,6 +558,8 @@ const imageNodeType = ({ */ const inProgressCopy = new Set() +let didShowTraceSVGRemovalWarningResize = false + const createFields = ({ pathPrefix, getNodeAndSavePathDependency, @@ -632,12 +644,19 @@ const createFields = ({ src: { type: GraphQLString }, tracedSVG: { type: GraphQLString, - resolve: parent => - getTracedSVG({ - ...parent, + resolve: async parent => { + if (!didShowTraceSVGRemovalWarningResize) { + console.warn( + `"tracedSVG" placeholder field is no longer supported (used in ImageSharp.resize processing), falling back to "base64". See https://gatsby.dev/tracesvg-removal/` + ) + didShowTraceSVGRemovalWarningResize = true + } + const { src } = await base64({ + file: parent.file, cache, - reporter, - }), + }) + return src + }, }, width: { type: GraphQLInt }, height: { type: GraphQLInt }, diff --git a/packages/gatsby-transformer-sharp/src/types.ts b/packages/gatsby-transformer-sharp/src/types.ts index 9c00f3042eb88..a3f1310ea9473 100644 --- a/packages/gatsby-transformer-sharp/src/types.ts +++ b/packages/gatsby-transformer-sharp/src/types.ts @@ -8,7 +8,6 @@ import { GraphQLNonNull, GraphQLInputFieldConfigMap, } from "gatsby/graphql" -import { Potrace } from "@gatsbyjs/potrace" import type Sharp from "sharp" const sharp: typeof Sharp = require(`./safe-sharp`) @@ -161,12 +160,12 @@ export const DuotoneGradientType = new GraphQLInputObjectType({ export const PotraceTurnPolicyType = new GraphQLEnumType({ name: `PotraceTurnPolicy`, values: { - TURNPOLICY_BLACK: { value: Potrace.TURNPOLICY_BLACK }, - TURNPOLICY_WHITE: { value: Potrace.TURNPOLICY_WHITE }, - TURNPOLICY_LEFT: { value: Potrace.TURNPOLICY_LEFT }, - TURNPOLICY_RIGHT: { value: Potrace.TURNPOLICY_RIGHT }, - TURNPOLICY_MINORITY: { value: Potrace.TURNPOLICY_MINORITY }, - TURNPOLICY_MAJORITY: { value: Potrace.TURNPOLICY_MAJORITY }, + TURNPOLICY_BLACK: { value: `black` }, + TURNPOLICY_WHITE: { value: `white` }, + TURNPOLICY_LEFT: { value: `left` }, + TURNPOLICY_RIGHT: { value: `right` }, + TURNPOLICY_MINORITY: { value: `minority` }, + TURNPOLICY_MAJORITY: { value: `majority` }, }, }) diff --git a/packages/gatsby/src/schema/__tests__/__snapshots__/print.js.snap b/packages/gatsby/src/schema/__tests__/__snapshots__/print.js.snap index 6e7c067c27b9d..f51d99052d35e 100644 --- a/packages/gatsby/src/schema/__tests__/__snapshots__/print.js.snap +++ b/packages/gatsby/src/schema/__tests__/__snapshots__/print.js.snap @@ -122,9 +122,9 @@ interface RemoteFile { \\"\\"\\" Format of generated placeholder image, displayed while the main image loads. - BLURRED: a blurred, low resolution image, encoded as a base64 data URI (default) - DOMINANT_COLOR: a solid color, calculated from the dominant color of the image. - TRACED_SVG: a low-resolution traced SVG of the image. + BLURRED: a blurred, low resolution image, encoded as a base64 data URI + DOMINANT_COLOR: a solid color, calculated from the dominant color of the image (default). + TRACED_SVG: deprecated. Will use DOMINANT_COLOR. NONE: no placeholder. Set the argument \\"backgroundColor\\" to use a fixed background color. \\"\\"\\" placeholder: RemoteFilePlaceholder = DOMINANT_COLOR @@ -511,9 +511,9 @@ interface RemoteFile { \\"\\"\\" Format of generated placeholder image, displayed while the main image loads. - BLURRED: a blurred, low resolution image, encoded as a base64 data URI (default) - DOMINANT_COLOR: a solid color, calculated from the dominant color of the image. - TRACED_SVG: a low-resolution traced SVG of the image. + BLURRED: a blurred, low resolution image, encoded as a base64 data URI + DOMINANT_COLOR: a solid color, calculated from the dominant color of the image (default). + TRACED_SVG: deprecated. Will use DOMINANT_COLOR. NONE: no placeholder. Set the argument \\"backgroundColor\\" to use a fixed background color. \\"\\"\\" placeholder: RemoteFilePlaceholder = DOMINANT_COLOR @@ -910,9 +910,9 @@ interface RemoteFile { \\"\\"\\" Format of generated placeholder image, displayed while the main image loads. - BLURRED: a blurred, low resolution image, encoded as a base64 data URI (default) - DOMINANT_COLOR: a solid color, calculated from the dominant color of the image. - TRACED_SVG: a low-resolution traced SVG of the image. + BLURRED: a blurred, low resolution image, encoded as a base64 data URI + DOMINANT_COLOR: a solid color, calculated from the dominant color of the image (default). + TRACED_SVG: deprecated. Will use DOMINANT_COLOR. NONE: no placeholder. Set the argument \\"backgroundColor\\" to use a fixed background color. \\"\\"\\" placeholder: RemoteFilePlaceholder = DOMINANT_COLOR diff --git a/yarn.lock b/yarn.lock index 04da1310a9db3..f7fff14e1aaaf 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1638,13 +1638,6 @@ unique-filename "^1.1.1" which "^1.3.1" -"@gatsbyjs/potrace@^2.3.0": - version "2.3.0" - resolved "https://registry.yarnpkg.com/@gatsbyjs/potrace/-/potrace-2.3.0.tgz#0ac22fb56a02ebc64ce55e4666c4b741cbf27377" - integrity sha512-72szhSY/4tPiPPOzq15CG6LW0s9FuWQ86gkLSUvBNoF0s+jsEdRaZmATYNjiY2Skg//EuyPLEqUQnXKXME0szg== - dependencies: - jimp-compact "^0.16.1-2" - "@gatsbyjs/reach-router@^2.0.0-v2.0": version "2.0.0-v2.0.2" resolved "https://registry.yarnpkg.com/@gatsbyjs/reach-router/-/reach-router-2.0.0-v2.0.2.tgz#6b7e4846e4c68113b8e454fd8ea027ffad5a68c8" @@ -14126,11 +14119,6 @@ jest@^27.4.4: import-local "^3.0.2" jest-cli "^27.4.4" -jimp-compact@^0.16.1-2: - version "0.16.1-2" - resolved "https://registry.yarnpkg.com/jimp-compact/-/jimp-compact-0.16.1-2.tgz#a82ff9a5a81f15a4b61b5e2e50fae6a43305e5a9" - integrity sha512-b2A3rRT1TITzqmaO70U2/uunCh43BQVq7BfRwGPkD5xj8/WZsR3sPTy9DENt+dNZGsel3zBEm1UtYegUxjZW7A== - joi@^14.3.1: version "14.3.1" resolved "https://registry.yarnpkg.com/joi/-/joi-14.3.1.tgz#164a262ec0b855466e0c35eea2a885ae8b6c703c" @@ -22330,7 +22318,7 @@ svgo@^1.0.0: unquote "~1.1.1" util.promisify "~1.0.0" -svgo@^2.3.0, svgo@^2.8.0: +svgo@^2.3.0: version "2.8.0" resolved "https://registry.yarnpkg.com/svgo/-/svgo-2.8.0.tgz#4ff80cce6710dc2795f0c7c74101e6764cfccd24" integrity sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==