diff options
Diffstat (limited to 'node_modules/postcss-svgo/src')
| -rw-r--r-- | node_modules/postcss-svgo/src/globals.d.ts | 1 | ||||
| -rw-r--r-- | node_modules/postcss-svgo/src/index.js | 140 | ||||
| -rw-r--r-- | node_modules/postcss-svgo/src/lib/url.js | 18 |
3 files changed, 159 insertions, 0 deletions
diff --git a/node_modules/postcss-svgo/src/globals.d.ts b/node_modules/postcss-svgo/src/globals.d.ts new file mode 100644 index 00000000..731c4638 --- /dev/null +++ b/node_modules/postcss-svgo/src/globals.d.ts @@ -0,0 +1 @@ +declare var URL: typeof import('url').URL; diff --git a/node_modules/postcss-svgo/src/index.js b/node_modules/postcss-svgo/src/index.js new file mode 100644 index 00000000..a40850c8 --- /dev/null +++ b/node_modules/postcss-svgo/src/index.js @@ -0,0 +1,140 @@ +'use strict'; +const valueParser = require('postcss-value-parser'); +const { optimize } = require('svgo'); +const { encode, decode } = require('./lib/url'); + +const PLUGIN = 'postcss-svgo'; +const dataURI = /data:image\/svg\+xml(;((charset=)?utf-8|base64))?,/i; +const dataURIBase64 = /data:image\/svg\+xml;base64,/i; + +// the following regex will globally match: +// \b([\w-]+) --> a word (a sequence of one or more [alphanumeric|underscore|dash] characters; followed by +// \s*=\s* --> an equal sign character (=) between optional whitespaces; followed by +// \\"([\S\s]+?)\\" --> any characters (including whitespaces and newlines) between literal escaped quotes (\") +const escapedQuotes = /\b([\w-]+)\s*=\s*\\"([\S\s]+?)\\"/g; + +/** + * @param {string} input the SVG string + * @param {Options} opts + * @return {{result: string, isUriEncoded: boolean}} the minification result + */ +function minifySVG(input, opts) { + let svg = input; + let decodedUri, isUriEncoded; + try { + decodedUri = decode(input); + isUriEncoded = decodedUri !== input; + } catch (e) { + // Swallow exception if we cannot decode the value + isUriEncoded = false; + } + + if (isUriEncoded) { + svg = /** @type {string} */ (decodedUri); + } + + if (opts.encode !== undefined) { + isUriEncoded = opts.encode; + } + + // normalize all escaped quote characters from svg attributes + // from <svg attr=\"value\"... /> to <svg attr="value"... /> + // see: https://github.com/cssnano/cssnano/issues/1194 + svg = svg.replace(escapedQuotes, '$1="$2"'); + + const result = optimize(svg, opts); + + return { + result: /** @type {import('svgo').Output}*/ (result).data, + isUriEncoded, + }; +} + +/** + * @param {import('postcss').Declaration} decl + * @param {Options} opts + * @param {import('postcss').Result} postcssResult + * @return {void} + */ +function minify(decl, opts, postcssResult) { + const parsed = valueParser(decl.value); + + const minified = parsed.walk((node) => { + if ( + node.type !== 'function' || + node.value.toLowerCase() !== 'url' || + !node.nodes.length + ) { + return; + } + let { value, quote } = /** @type {valueParser.StringNode} */ ( + node.nodes[0] + ); + + let optimizedValue; + + try { + if (dataURIBase64.test(value)) { + const url = new URL(value); + const base64String = `${url.protocol}${url.pathname}`.replace( + dataURI, + '' + ); + const svg = Buffer.from(base64String, 'base64').toString('utf8'); + const { result } = minifySVG(svg, opts); + const data = Buffer.from(result).toString('base64'); + optimizedValue = 'data:image/svg+xml;base64,' + data + url.hash; + } else if (dataURI.test(value)) { + const svg = value.replace(dataURI, ''); + const { result, isUriEncoded } = minifySVG(svg, opts); + let data = isUriEncoded ? encode(result) : result; + // Should always encode # otherwise we yield a broken SVG + // in Firefox (works in Chrome however). See this issue: + // https://github.com/cssnano/cssnano/issues/245 + data = data.replace(/#/g, '%23'); + optimizedValue = 'data:image/svg+xml;charset=utf-8,' + data; + quote = isUriEncoded ? '"' : "'"; + } else { + return; + } + } catch (error) { + decl.warn(postcssResult, `${error}`); + return; + } + node.nodes[0] = Object.assign({}, node.nodes[0], { + value: optimizedValue, + quote: quote, + type: 'string', + before: '', + after: '', + }); + + return false; + }); + + decl.value = minified.toString(); +} +/** @typedef {{encode?: boolean, plugins?: object[]} & import('svgo').Config} Options */ +/** + * @type {import('postcss').PluginCreator<Options>} + * @param {Options} opts + * @return {import('postcss').Plugin} + */ +function pluginCreator(opts = {}) { + return { + postcssPlugin: PLUGIN, + + OnceExit(css, { result }) { + css.walkDecls((decl) => { + if (!dataURI.test(decl.value)) { + return; + } + + minify(decl, opts, result); + }); + }, + }; +} + +pluginCreator.postcss = true; +module.exports = pluginCreator; diff --git a/node_modules/postcss-svgo/src/lib/url.js b/node_modules/postcss-svgo/src/lib/url.js new file mode 100644 index 00000000..1db6a397 --- /dev/null +++ b/node_modules/postcss-svgo/src/lib/url.js @@ -0,0 +1,18 @@ +'use strict'; +/** + * @param {string} data + * @return {string} + */ +function encode(data) { + return data + .replace(/"/g, "'") + .replace(/%/g, '%25') + .replace(/</g, '%3C') + .replace(/>/g, '%3E') + .replace(/&/g, '%26') + .replace(/#/g, '%23') + .replace(/\s+/g, ' '); +} + +const decode = decodeURIComponent; +module.exports = { encode, decode }; |
