From 22d2a18be178a7e0e30dc076a5cc12e15c112af9 Mon Sep 17 00:00:00 2001 From: Ludovico Fischer Date: Wed, 31 Mar 2021 23:37:38 +0200 Subject: [PATCH 1/2] refactor: remove is-svg dependency As there is no practical difference between an invalid SVG and an SVG that SVGO cannot parse, just try to parse input with SVGO directly. Also remove dependency on extra XML parsers. --- packages/postcss-svgo/package.json | 3 +- packages/postcss-svgo/src/__tests__/index.js | 21 ++--- packages/postcss-svgo/src/index.js | 8 +- packages/postcss-svgo/yarn.lock | 92 +++++++++----------- 4 files changed, 51 insertions(+), 73 deletions(-) diff --git a/packages/postcss-svgo/package.json b/packages/postcss-svgo/package.json index b92d19972..4f2c38171 100644 --- a/packages/postcss-svgo/package.json +++ b/packages/postcss-svgo/package.json @@ -30,9 +30,8 @@ }, "repository": "cssnano/cssnano", "dependencies": { - "is-svg": "^4.2.0", "postcss-value-parser": "^4.1.0", - "svgo": "^2.2.2" + "svgo": "^2.3.0" }, "bugs": { "url": "https://github.com/cssnano/cssnano/issues" diff --git a/packages/postcss-svgo/src/__tests__/index.js b/packages/postcss-svgo/src/__tests__/index.js index ddc6e33f6..f5f80bfff 100644 --- a/packages/postcss-svgo/src/__tests__/index.js +++ b/packages/postcss-svgo/src/__tests__/index.js @@ -1,5 +1,4 @@ import { readFileSync as file } from 'fs'; -import postcss from 'postcss'; import filters from 'pleeease-filters'; import { extendDefaultPlugins } from 'svgo'; import plugin from '../'; @@ -190,6 +189,13 @@ test( ) ); +test( + 'should skip svgs containing unclosed tags', + passthroughCSS( + 'h1{background:url(data:image/svg+xml;charset=utf-8,style type="text/css"> svg { fill: red; } )}' + ) +); + test( 'should pass through links to svg files', passthroughCSS('h1{background:url(unicorn.svg)}') @@ -203,19 +209,6 @@ test( ) ); -test('should reject on malformed svgs', async () => { - expect.assertions(1); - - const css = - 'h1{background:url(data:image/svg+xml;charset=utf-8,style type="text/css"> svg { fill: red; } )}'; - - try { - await postcss(plugin()).process(css, { from: undefined }); - } catch (error) { - expect(error.message).toMatch(/Unexpected close tag/); - } -}); - test('should not crash on malformed urls when encoded', () => { const svg = encode(file(`${__dirname}/border.svg`, 'utf-8')); diff --git a/packages/postcss-svgo/src/index.js b/packages/postcss-svgo/src/index.js index 3760beef2..c6ec8535e 100644 --- a/packages/postcss-svgo/src/index.js +++ b/packages/postcss-svgo/src/index.js @@ -1,6 +1,5 @@ import valueParser from 'postcss-value-parser'; import { optimize } from 'svgo'; -import isSvg from 'is-svg'; import { encode, decode } from './lib/url'; const PLUGIN = 'postcss-svgo'; @@ -48,14 +47,13 @@ function minify(decl, opts) { } } - if (!isSvg(svg)) { - return; - } - let result; try { result = optimize(svg, opts); if (result.error) { + if (result.error.startsWith('Error in parsing SVG')) { + return; + } throw new Error(`${PLUGIN}: ${result.error}`); } } catch (error) { diff --git a/packages/postcss-svgo/yarn.lock b/packages/postcss-svgo/yarn.lock index 45d761b95..6de59417d 100644 --- a/packages/postcss-svgo/yarn.lock +++ b/packages/postcss-svgo/yarn.lock @@ -39,15 +39,15 @@ color-name@~1.1.4: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== -colorette@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.1.tgz#4d0b921325c14faf92633086a536db6e89564b1b" - integrity sha512-puCDz0CzydiSYOrnXpz/PKd69zRrribezjtE9yd4zvytoRc8+RY/KJPvtPFKZS3E3wP6neGyMe0vOTlHO5L3Pw== +colorette@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.2.tgz#cbcc79d5e99caea2dbf10eb3a26fd8b3e6acfa94" + integrity sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w== commander@^7.1.0: - version "7.1.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-7.1.0.tgz#f2eaecf131f10e36e07d894698226e36ae0eb5ff" - integrity sha512-pRxBna3MJe6HKnBGsDyMv8ETbptw3axEdYHoqNh7gu5oDcew8fs0xnivZGm06Ogk8zGAJ9VX+OPEr2GXEQK4dg== + version "7.2.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" + integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== css-select@^3.1.2: version "3.1.2" @@ -61,9 +61,9 @@ css-select@^3.1.2: nth-check "^2.0.0" css-tree@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.1.2.tgz#9ae393b5dafd7dae8a622475caec78d3d8fbd7b5" - integrity sha512-wCoWush5Aeo48GLhfHPbmvZs59Z+M7k5+B1xDnXbdWNcEF423DoFdqSWE0PM5aNk5nI5cp1q7ms36zGApY/sKQ== + version "1.1.3" + resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.1.3.tgz#eb4870fb6fd7707327ec95c2ff2ab09b5e8db91d" + integrity sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q== dependencies: mdn-data "2.0.14" source-map "^0.6.1" @@ -89,58 +89,46 @@ dom-serializer@^1.0.1: domhandler "^4.0.0" entities "^2.0.0" -domelementtype@^2.0.1, domelementtype@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.1.0.tgz#a851c080a6d1c3d94344aed151d99f669edf585e" - integrity sha512-LsTgx/L5VpD+Q8lmsXSHW2WpA+eBlZ9HPf3erD1IoPF00/3JKHZ3BknUVA2QGDNu69ZNmyFmCWBSO45XjYKC5w== +domelementtype@^2.0.1, domelementtype@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.2.0.tgz#9a0b6c2782ed6a1c7323d42267183df9bd8b1d57" + integrity sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A== -domhandler@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.0.0.tgz#01ea7821de996d85f69029e81fa873c21833098e" - integrity sha512-KPTbnGQ1JeEMQyO1iYXoagsI6so/C96HZiFyByU3T6iAzpXn8EGEvct6unm1ZGoed8ByO2oirxgwxBmqKF9haA== +domhandler@^4.0.0, domhandler@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.1.0.tgz#c1d8d494d5ec6db22de99e46a149c2a4d23ddd43" + integrity sha512-/6/kmsGlMY4Tup/nGVutdrK9yQi4YjWVcVeoQmixpzjOUK1U7pQkvAPHBJeUxOgxF0J8f8lwCJSlCfD0V4CMGQ== dependencies: - domelementtype "^2.1.0" + domelementtype "^2.2.0" domutils@^2.4.3: - version "2.4.4" - resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.4.4.tgz#282739c4b150d022d34699797369aad8d19bbbd3" - integrity sha512-jBC0vOsECI4OMdD0GC9mGn7NXPLb+Qt6KW1YDQzeQYRUFKmNG8lh7mO5HiELfr+lLQE7loDVI4QcAxV80HS+RA== + version "2.5.1" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.5.1.tgz#9b8e84b5d9f788499ae77506ea832e9b4f9aa1c0" + integrity sha512-hO1XwHMGAthA/1KL7c83oip/6UWo3FlUNIuWiWKltoiQ5oCOiqths8KknvY2jpOohUoUgnwa/+Rm7UpwpSbY/Q== dependencies: dom-serializer "^1.0.1" - domelementtype "^2.0.1" - domhandler "^4.0.0" + domelementtype "^2.2.0" + domhandler "^4.1.0" entities@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/entities/-/entities-2.1.0.tgz#992d3129cf7df6870b96c57858c249a120f8b8b5" - integrity sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w== + version "2.2.0" + resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55" + integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A== has-flag@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== -html-comment-regex@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/html-comment-regex/-/html-comment-regex-1.1.2.tgz#97d4688aeb5c81886a364faa0cad1dda14d433a7" - integrity sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ== - -is-svg@^4.2.0: - version "4.2.1" - resolved "https://registry.yarnpkg.com/is-svg/-/is-svg-4.2.1.tgz#095b496e345fec9211c2a7d5d021003e040d6f81" - integrity sha512-PHx3ANecKsKNl5y5+Jvt53Y4J7MfMpbNZkv384QNiswMKAWIbvcqbPz+sYbFKJI8Xv3be01GSFniPmoaP+Ai5A== - dependencies: - html-comment-regex "^1.1.2" - mdn-data@2.0.14: version "2.0.14" resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.14.tgz#7113fc4281917d63ce29b43446f701e68c25ba50" integrity sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow== -nanoid@^3.1.20: - version "3.1.20" - resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.20.tgz#badc263c6b1dcf14b71efaa85f6ab4c1d6cfc788" - integrity sha512-a1cQNyczgKbLX9jwbS/+d7W8fX/RfgYR7lVWwWOGIPNgK2m0MWvrGF6/m4kk6U3QcFMnZf3RIhL0v2Jgh/0Uxw== +nanoid@^3.1.22: + version "3.1.22" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.22.tgz#b35f8fb7d151990a8aebd5aa5015c03cf726f844" + integrity sha512-/2ZUaJX2ANuLtTvqTlgqBQNJoQO398KyJgZloL0PZkC0dpysjncRUPsFe3DUPzz/y3h+u7C46np8RMuvF3jsSQ== nth-check@^2.0.0: version "2.0.0" @@ -155,12 +143,12 @@ postcss-value-parser@^4.1.0: integrity sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ== postcss@^8.2.1: - version "8.2.1" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.2.1.tgz#eabc5557c4558059b9d9e5b15bce7ffa9089c2a8" - integrity sha512-RhsqOOAQzTgh1UB/IZdca7F9WDb7SUCR2Vnv1x7DbvuuggQIpoDwjK+q0rzoPffhYvWNKX5JSwS4so4K3UC6vA== + version "8.2.9" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.2.9.tgz#fd95ff37b5cee55c409b3fdd237296ab4096fba3" + integrity sha512-b+TmuIL4jGtCHtoLi+G/PisuIl9avxs8IZMSmlABRwNz5RLUUACrC+ws81dcomz1nRezm5YPdXiMEzBEKgYn+Q== dependencies: - colorette "^1.2.1" - nanoid "^3.1.20" + colorette "^1.2.2" + nanoid "^3.1.22" source-map "^0.6.1" source-map@^0.6.1: @@ -180,10 +168,10 @@ supports-color@^7.1.0: dependencies: has-flag "^4.0.0" -svgo@^2.2.2: - version "2.2.2" - resolved "https://registry.yarnpkg.com/svgo/-/svgo-2.2.2.tgz#51d67c7149661282d22a3c8683f4795cdb40f687" - integrity sha512-kJugY2d0yrsONnG4YavppVkKmKULMw2iFRbB9+usyWqzTaqoBuUaqdMnQ2G1n5P1dmOA2tZvc5zmMM6sPOVBSQ== +svgo@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/svgo/-/svgo-2.3.0.tgz#6b3af81d0cbd1e19c83f5f63cec2cb98c70b5373" + integrity sha512-fz4IKjNO6HDPgIQxu4IxwtubtbSfGEAJUq/IXyTPIkGhWck/faiiwfkvsB8LnBkKLvSoyNNIY6d13lZprJMc9Q== dependencies: "@trysound/sax" "0.1.1" chalk "^4.1.0" From fc8a52a9603606b27dedd426890ce016198cc426 Mon Sep 17 00:00:00 2001 From: Ludovico Fischer Date: Fri, 2 Apr 2021 17:56:55 +0200 Subject: [PATCH 2/2] refactor(postcss-svgo): always warn on invalid SVG Handle all SVGO failures in the same way, as there is no practical difference between being unable to parse and being unable to optimise. --- packages/postcss-svgo/src/__tests__/index.js | 14 ++++++++------ packages/postcss-svgo/src/index.js | 15 +++++++-------- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/packages/postcss-svgo/src/__tests__/index.js b/packages/postcss-svgo/src/__tests__/index.js index f5f80bfff..54f6a5008 100644 --- a/packages/postcss-svgo/src/__tests__/index.js +++ b/packages/postcss-svgo/src/__tests__/index.js @@ -1,4 +1,5 @@ import { readFileSync as file } from 'fs'; +import postcss from 'postcss'; import filters from 'pleeease-filters'; import { extendDefaultPlugins } from 'svgo'; import plugin from '../'; @@ -189,12 +190,13 @@ test( ) ); -test( - 'should skip svgs containing unclosed tags', - passthroughCSS( - 'h1{background:url(data:image/svg+xml;charset=utf-8,style type="text/css"> svg { fill: red; } )}' - ) -); +test('should warn on SVG containing unclosed tags', async () => { + const css = + 'h1{background:url(data:image/svg+xml;charset=utf-8,style type="text/css"> svg { fill: red; } )}'; + const result = await postcss(plugin()).process(css, { from: undefined }); + expect(result.messages.length).toBe(1); + expect(result.messages[0].type).toBe('warning'); +}); test( 'should pass through links to svg files', diff --git a/packages/postcss-svgo/src/index.js b/packages/postcss-svgo/src/index.js index c6ec8535e..24c9bf529 100644 --- a/packages/postcss-svgo/src/index.js +++ b/packages/postcss-svgo/src/index.js @@ -6,7 +6,7 @@ const PLUGIN = 'postcss-svgo'; const dataURI = /data:image\/svg\+xml(;((charset=)?utf-8|base64))?,/i; const dataURIBase64 = /data:image\/svg\+xml;base64,/i; -function minify(decl, opts) { +function minify(decl, opts, postcssResult) { const parsed = valueParser(decl.value); decl.value = parsed.walk((node) => { @@ -51,13 +51,12 @@ function minify(decl, opts) { try { result = optimize(svg, opts); if (result.error) { - if (result.error.startsWith('Error in parsing SVG')) { - return; - } - throw new Error(`${PLUGIN}: ${result.error}`); + decl.warn(postcssResult, `${result.error}`); + return; } } catch (error) { - throw new Error(`${PLUGIN}: ${error}`); + decl.warn(postcssResult, `${error}`); + return; } let data, optimizedValue; @@ -92,13 +91,13 @@ function pluginCreator(opts = {}) { return { postcssPlugin: PLUGIN, - OnceExit(css) { + OnceExit(css, { result }) { css.walkDecls((decl) => { if (!dataURI.test(decl.value)) { return; } - minify(decl, opts); + minify(decl, opts, result); }); }, };