svgo
Advanced tools
Comparing version 3.0.3 to 3.0.4
@@ -51,2 +51,3 @@ 'use strict'; | ||
require('../plugins/removeViewBox.js'), | ||
require('../plugins/removeXlink.js'), | ||
require('../plugins/removeXMLNS.js'), | ||
@@ -53,0 +54,0 @@ require('../plugins/removeXMLProcInst.js'), |
@@ -18,3 +18,3 @@ 'use strict'; | ||
const SAX = require('@trysound/sax'); | ||
const { textElems } = require('../plugins/_collections.js'); | ||
const { textElems } = require('../plugins/_collections'); | ||
@@ -21,0 +21,0 @@ class SvgoParserError extends Error { |
@@ -15,3 +15,3 @@ 'use strict'; | ||
const { textElems } = require('../plugins/_collections.js'); | ||
const { textElems } = require('../plugins/_collections'); | ||
@@ -37,5 +37,3 @@ /** | ||
/** | ||
* @type {Options} | ||
*/ | ||
/** @type {Options} */ | ||
const defaults = { | ||
@@ -63,3 +61,3 @@ doctypeStart: '<!DOCTYPE', | ||
regValEntities: /[&"<>]/g, | ||
encodeEntity: encodeEntity, | ||
encodeEntity, | ||
pretty: false, | ||
@@ -71,5 +69,3 @@ useShortTags: true, | ||
/** | ||
* @type {Record<string, string>} | ||
*/ | ||
/** @type {Record<string, string>} */ | ||
const entities = { | ||
@@ -120,3 +116,3 @@ '&': '&', | ||
let svg = stringifyNode(data, config, state); | ||
if (config.finalNewline && svg.length > 0 && svg[svg.length - 1] !== '\n') { | ||
if (config.finalNewline && svg.length > 0 && !svg.endsWith('\n')) { | ||
svg += eol; | ||
@@ -123,0 +119,0 @@ } |
@@ -17,2 +17,3 @@ 'use strict'; | ||
const csstree = require('css-tree'); | ||
const csswhat = require('css-what'); | ||
const { | ||
@@ -92,3 +93,6 @@ // @ts-ignore internal api | ||
if (cssNode.type === 'Atrule') { | ||
if (cssNode.name === 'keyframes') { | ||
if ( | ||
cssNode.name === 'keyframes' || | ||
cssNode.name === '-webkit-keyframes' | ||
) { | ||
return csstreeWalkSkip; | ||
@@ -278,1 +282,55 @@ } | ||
exports.computeStyle = computeStyle; | ||
/** | ||
* Determines if the CSS selector includes or traverses the given attribute. | ||
* | ||
* Classes and IDs are generated as attribute selectors, so you can check for | ||
* if a `.class` or `#id` is included by passing `name=class` or `name=id` | ||
* respectively. | ||
* | ||
* @param {csstree.ListItem<csstree.CssNode>|string} selector | ||
* @param {string} name | ||
* @param {?string} value | ||
* @param {boolean} traversed | ||
* @returns {boolean} | ||
*/ | ||
const includesAttrSelector = ( | ||
selector, | ||
name, | ||
value = null, | ||
traversed = false | ||
) => { | ||
const selectors = | ||
typeof selector === 'string' | ||
? csswhat.parse(selector) | ||
: csswhat.parse(csstree.generate(selector.data)); | ||
for (const subselector of selectors) { | ||
const hasAttrSelector = subselector.some((segment, index) => { | ||
if (traversed) { | ||
if (index === subselector.length - 1) { | ||
return false; | ||
} | ||
const isNextTraversal = csswhat.isTraversal(subselector[index + 1]); | ||
if (!isNextTraversal) { | ||
return false; | ||
} | ||
} | ||
if (segment.type !== 'attribute' || segment.name !== name) { | ||
return false; | ||
} | ||
return value == null ? true : segment.value === value; | ||
}); | ||
if (hasAttrSelector) { | ||
return true; | ||
} | ||
} | ||
return false; | ||
}; | ||
exports.includesAttrSelector = includesAttrSelector; |
@@ -179,1 +179,16 @@ 'use strict'; | ||
exports.hasScripts = hasScripts; | ||
/** | ||
* For example, a string that contains one or more of following would match and | ||
* return true: | ||
* | ||
* * `url(#gradient001)` | ||
* * `url('#gradient001')` | ||
* | ||
* @param {string} body | ||
* @returns {boolean} If the given string includes a URL reference. | ||
*/ | ||
const includesUrlReference = (body) => { | ||
return /\burl\((["'])?#(.+?)\1\)/g.test(body); | ||
}; | ||
exports.includesUrlReference = includesUrlReference; |
{ | ||
"packageManager": "yarn@2.4.3", | ||
"name": "svgo", | ||
"version": "3.0.3", | ||
"version": "3.0.4", | ||
"description": "Nodejs-based tool for optimizing SVG vector graphics files", | ||
@@ -118,2 +118,3 @@ "license": "MIT", | ||
"css-tree": "^2.2.1", | ||
"css-what": "^6.1.0", | ||
"csso": "5.0.5", | ||
@@ -120,0 +121,0 @@ "picocolors": "^1.0.0" |
@@ -96,9 +96,15 @@ 'use strict'; | ||
exports.textElems = exports.elemsGroups.textContent.concat('title'); | ||
/** | ||
* Elements where adding or removing whitespace may effect rendering, metadata, | ||
* or semantic meaning. | ||
* | ||
* @see https://developer.mozilla.org/docs/Web/HTML/Element/pre | ||
*/ | ||
exports.textElems = [...exports.elemsGroups.textContent, 'title', 'pre']; | ||
exports.pathElems = ['path', 'glyph', 'missing-glyph']; | ||
// https://www.w3.org/TR/SVG11/intro.html#Definitions | ||
/** | ||
* @type {Record<string, Array<string>>} | ||
* @see https://www.w3.org/TR/SVG11/intro.html#Definitions | ||
*/ | ||
@@ -367,3 +373,2 @@ exports.attrsGroups = { | ||
// https://www.w3.org/TR/SVG11/eltindex.html | ||
/** | ||
@@ -377,2 +382,3 @@ * @type {Record<string, { | ||
* }>} | ||
* @see https://www.w3.org/TR/SVG11/eltindex.html | ||
*/ | ||
@@ -1954,3 +1960,5 @@ exports.elems = { | ||
// https://www.w3.org/TR/SVG11/linking.html#processingIRI | ||
/** | ||
* @see https://www.w3.org/TR/SVG11/linking.html#processingIRI | ||
*/ | ||
exports.referencesProps = [ | ||
@@ -1969,3 +1977,5 @@ 'clip-path', | ||
// https://www.w3.org/TR/SVG11/propidx.html | ||
/** | ||
* @see https://www.w3.org/TR/SVG11/propidx.html | ||
*/ | ||
exports.inheritableAttrs = [ | ||
@@ -2224,3 +2234,5 @@ 'clip-rule', | ||
// https://www.w3.org/TR/SVG11/single-page.html#types-DataTypeColor | ||
/** | ||
* @see https://www.w3.org/TR/SVG11/single-page.html#types-DataTypeColor | ||
*/ | ||
exports.colorsProps = [ | ||
@@ -2227,0 +2239,0 @@ 'color', |
@@ -15,3 +15,6 @@ 'use strict'; | ||
const { path2js } = require('./_path.js'); | ||
const { removeLeadingZero } = require('../lib/svgo/tools.js'); | ||
const { | ||
removeLeadingZero, | ||
includesUrlReference, | ||
} = require('../lib/svgo/tools.js'); | ||
const { referencesProps, attrsGroupsDefaults } = require('./_collections.js'); | ||
@@ -39,5 +42,2 @@ | ||
enter: (node) => { | ||
const computedStyle = computeStyle(stylesheet, node); | ||
// used only for paths for now | ||
if (node.attributes.d == null) { | ||
@@ -53,3 +53,3 @@ return; | ||
// if there are no 'stroke' attr and references to other objects such as | ||
// gradiends or clip-path which are also subjects to transform. | ||
// gradients or clip-path which are also subjects to transform. | ||
if ( | ||
@@ -63,3 +63,3 @@ node.attributes.transform == null || | ||
([name, value]) => | ||
referencesProps.includes(name) && value.includes('url(') | ||
referencesProps.includes(name) && includesUrlReference(value) | ||
) | ||
@@ -70,7 +70,19 @@ ) { | ||
const computedStyle = computeStyle(stylesheet, node); | ||
const transformStyle = computedStyle.transform; | ||
// Transform overridden in <style> tag which is not considered | ||
if ( | ||
transformStyle.type === 'static' && | ||
transformStyle.value !== node.attributes.transform | ||
) { | ||
return; | ||
} | ||
const matrix = transformsMultiply( | ||
transform2js(node.attributes.transform) | ||
); | ||
const stroke = | ||
computedStyle.stroke != null && computedStyle.stroke.type === 'static' | ||
computedStyle.stroke?.type === 'static' | ||
? computedStyle.stroke.value | ||
@@ -80,4 +92,3 @@ : null; | ||
const strokeWidth = | ||
computedStyle['stroke-width'] != null && | ||
computedStyle['stroke-width'].type === 'static' | ||
computedStyle['stroke-width']?.type === 'static' | ||
? computedStyle['stroke-width'].value | ||
@@ -88,6 +99,4 @@ : null; | ||
if ( | ||
(computedStyle.stroke != null && | ||
computedStyle.stroke.type === 'dynamic') || | ||
(computedStyle.strokeWidth != null && | ||
computedStyle['stroke-width'].type === 'dynamic') | ||
computedStyle.stroke?.type === 'dynamic' || | ||
computedStyle['stroke-width']?.type === 'dynamic' | ||
) { | ||
@@ -104,3 +113,3 @@ return; | ||
if (stroke && stroke != 'none') { | ||
if (params.applyTransformsStroked === false) { | ||
if (!params.applyTransformsStroked) { | ||
return; | ||
@@ -107,0 +116,0 @@ } |
@@ -25,4 +25,4 @@ 'use strict'; | ||
* @type {import('./plugins-types').Plugin<'convertOneStopGradients'>} | ||
* @see https://developer.mozilla.org/en-US/docs/Web/SVG/Element/linearGradient | ||
* @see https://developer.mozilla.org/en-US/docs/Web/SVG/Element/radialGradient | ||
* @see https://developer.mozilla.org/docs/Web/SVG/Element/linearGradient | ||
* @see https://developer.mozilla.org/docs/Web/SVG/Element/radialGradient | ||
*/ | ||
@@ -29,0 +29,0 @@ exports.fn = (root) => { |
@@ -49,2 +49,3 @@ 'use strict'; | ||
* lineShorthands: boolean, | ||
* convertToZ: boolean, | ||
* curveSmoothShorthands: boolean, | ||
@@ -99,2 +100,3 @@ * floatPrecision: number | false, | ||
lineShorthands = true, | ||
convertToZ = true, | ||
curveSmoothShorthands = true, | ||
@@ -121,2 +123,3 @@ floatPrecision = 3, | ||
lineShorthands, | ||
convertToZ, | ||
curveSmoothShorthands, | ||
@@ -173,2 +176,8 @@ floatPrecision, | ||
const maybeHasStrokeAndLinecap = maybeHasStroke && maybeHasLinecap; | ||
const isSafeToUseZ = maybeHasStroke | ||
? computedStyle['stroke-linecap']?.type === 'static' && | ||
computedStyle['stroke-linecap'].value === 'round' && | ||
computedStyle['stroke-linejoin']?.type === 'static' && | ||
computedStyle['stroke-linejoin'].value === 'round' | ||
: true; | ||
@@ -182,2 +191,3 @@ var data = path2js(node); | ||
data = filters(data, newParams, { | ||
isSafeToUseZ, | ||
maybeHasStrokeAndLinecap, | ||
@@ -379,6 +389,10 @@ hasMarkerMid, | ||
* params: InternalParams, | ||
* aux: { maybeHasStrokeAndLinecap: boolean, hasMarkerMid: boolean } | ||
* aux: { isSafeToUseZ: boolean, maybeHasStrokeAndLinecap: boolean, hasMarkerMid: boolean } | ||
* ) => PathDataItem[]} | ||
*/ | ||
function filters(path, params, { maybeHasStrokeAndLinecap, hasMarkerMid }) { | ||
function filters( | ||
path, | ||
params, | ||
{ isSafeToUseZ, maybeHasStrokeAndLinecap, hasMarkerMid } | ||
) { | ||
var stringify = data2Path.bind(null, params), | ||
@@ -673,2 +687,16 @@ relSubpoint = [0, 0], | ||
// convert going home to z | ||
// m 0 0 h 5 v 5 l -5 -5 -> m 0 0 h 5 v 5 z | ||
if ( | ||
params.convertToZ && | ||
(isSafeToUseZ || next?.command === 'Z' || next?.command === 'z') && | ||
(command === 'l' || command === 'h' || command === 'v') | ||
) { | ||
// @ts-ignore | ||
if (pathBase[0] === item.coords[0] && pathBase[1] === item.coords[1]) { | ||
command = 'z'; | ||
data = []; | ||
} | ||
} | ||
// collapse repeated commands | ||
@@ -816,2 +844,12 @@ // h 20 h 30 -> h 50 | ||
} | ||
if ( | ||
(command === 'Z' || command === 'z') && | ||
params.removeUseless && | ||
isSafeToUseZ && | ||
// @ts-ignore | ||
item.base[0] === item.coords[0] && | ||
// @ts-ignore | ||
item.base[1] === item.coords[1] | ||
) | ||
return false; | ||
@@ -818,0 +856,0 @@ return true; |
'use strict'; | ||
/** | ||
* @typedef {import('../lib/types').Specificity} Specificity | ||
* @typedef {import('../lib/types').XastElement} XastElement | ||
@@ -19,3 +18,4 @@ * @typedef {import('../lib/types').XastParent} XastParent | ||
} = require('../lib/xast.js'); | ||
const { compareSpecificity } = require('../lib/style'); | ||
const { compareSpecificity, includesAttrSelector } = require('../lib/style'); | ||
const { attrsGroups } = require('./_collections'); | ||
@@ -56,11 +56,8 @@ exports.name = 'inlineStyles'; | ||
enter: (node, parentNode) => { | ||
// skip <foreignObject /> content | ||
if (node.name === 'foreignObject') { | ||
return visitSkip; | ||
} | ||
// collect only non-empty <style /> elements | ||
if (node.name !== 'style' || node.children.length === 0) { | ||
return; | ||
} | ||
// values other than the empty string or text/css are not used | ||
if ( | ||
@@ -73,12 +70,10 @@ node.attributes.type != null && | ||
} | ||
// parse css in style element | ||
let cssText = ''; | ||
for (const child of node.children) { | ||
if (child.type === 'text' || child.type === 'cdata') { | ||
cssText += child.value; | ||
} | ||
} | ||
/** | ||
* @type {?csstree.CssNode} | ||
*/ | ||
const cssText = node.children | ||
.filter((child) => child.type === 'text' || child.type === 'cdata') | ||
// @ts-ignore | ||
.map((child) => child.value) | ||
.join(''); | ||
/** @type {?csstree.CssNode} */ | ||
let cssAst = null; | ||
@@ -108,10 +103,10 @@ try { | ||
// skip media queries not included into useMqs param | ||
let mq = ''; | ||
let mediaQuery = ''; | ||
if (atrule != null) { | ||
mq = atrule.name; | ||
mediaQuery = atrule.name; | ||
if (atrule.prelude != null) { | ||
mq += ` ${csstree.generate(atrule.prelude)}`; | ||
mediaQuery += ` ${csstree.generate(atrule.prelude)}`; | ||
} | ||
} | ||
if (useMqs.includes(mq) === false) { | ||
if (!useMqs.includes(mediaQuery)) { | ||
return; | ||
@@ -145,3 +140,4 @@ } | ||
}); | ||
if (usePseudos.includes(pseudoSelectors) === false) { | ||
if (!usePseudos.includes(pseudoSelectors)) { | ||
return; | ||
@@ -168,4 +164,4 @@ } | ||
} | ||
// stable sort selectors | ||
const sortedSelectors = [...selectors] | ||
const sortedSelectors = selectors | ||
.slice() | ||
.sort((a, b) => { | ||
@@ -216,5 +212,13 @@ const aSpecificity = specificity(a.item.data); | ||
const styleDeclarationItems = new Map(); | ||
/** @type {csstree.ListItem<csstree.CssNode>} */ | ||
let firstListItem; | ||
csstree.walk(styleDeclarationList, { | ||
visit: 'Declaration', | ||
enter(node, item) { | ||
if (firstListItem == null) { | ||
firstListItem = item; | ||
} | ||
styleDeclarationItems.set(node.property.toLowerCase(), item); | ||
@@ -231,9 +235,21 @@ }, | ||
// inline styles, external styles higher priority than inline styles, external styles used | ||
const matchedItem = styleDeclarationItems.get( | ||
ruleDeclaration.property | ||
); | ||
const property = ruleDeclaration.property; | ||
if ( | ||
attrsGroups.presentation.includes(property) && | ||
!selectors.some((selector) => | ||
includesAttrSelector(selector.item, property) | ||
) | ||
) { | ||
delete selectedEl.attributes[property]; | ||
} | ||
const matchedItem = styleDeclarationItems.get(property); | ||
const ruleDeclarationItem = | ||
styleDeclarationList.children.createItem(ruleDeclaration); | ||
if (matchedItem == null) { | ||
styleDeclarationList.children.append(ruleDeclarationItem); | ||
styleDeclarationList.children.insert( | ||
ruleDeclarationItem, | ||
firstListItem | ||
); | ||
} else if ( | ||
@@ -247,6 +263,3 @@ matchedItem.data.important !== true && | ||
); | ||
styleDeclarationItems.set( | ||
ruleDeclaration.property, | ||
ruleDeclarationItem | ||
); | ||
styleDeclarationItems.set(property, ruleDeclarationItem); | ||
} | ||
@@ -274,3 +287,3 @@ }, | ||
// no further processing required | ||
if (removeMatchedSelectors === false) { | ||
if (!removeMatchedSelectors) { | ||
return; | ||
@@ -299,3 +312,8 @@ } | ||
for (const child of selector.node.children) { | ||
if (child.type === 'ClassSelector') { | ||
if ( | ||
child.type === 'ClassSelector' && | ||
!selectors.some((selector) => | ||
includesAttrSelector(selector.item, 'class', child.name, true) | ||
) | ||
) { | ||
classList.delete(child.name); | ||
@@ -314,5 +332,12 @@ } | ||
if ( | ||
firstSubSelector != null && | ||
firstSubSelector.type === 'IdSelector' && | ||
selectedEl.attributes.id === firstSubSelector.name | ||
firstSubSelector?.type === 'IdSelector' && | ||
selectedEl.attributes.id === firstSubSelector.name && | ||
!selectors.some((selector) => | ||
includesAttrSelector( | ||
selector.item, | ||
'id', | ||
firstSubSelector.name, | ||
true | ||
) | ||
) | ||
) { | ||
@@ -319,0 +344,0 @@ delete selectedEl.attributes.id; |
'use strict'; | ||
const { pathElems, referencesProps } = require('./_collections.js'); | ||
const { includesUrlReference } = require('../lib/svgo/tools.js'); | ||
@@ -39,3 +40,3 @@ exports.name = 'moveGroupAttrsToElems'; | ||
([name, value]) => | ||
referencesProps.includes(name) && value.includes('url(') | ||
referencesProps.includes(name) && includesUrlReference(value) | ||
) === false && | ||
@@ -42,0 +43,0 @@ node.children.every( |
@@ -45,2 +45,3 @@ import type { | ||
lineShorthands?: boolean; | ||
convertToZ?: boolean; | ||
curveSmoothShorthands?: boolean; | ||
@@ -142,3 +143,3 @@ floatPrecision?: number | false; | ||
removeComments: { | ||
preservePatterns: Array<RegExp|string> | false | ||
preservePatterns: Array<RegExp | string> | false; | ||
}; | ||
@@ -245,2 +246,12 @@ removeDesc: { | ||
removeStyleElement: void; | ||
removeXlink: { | ||
/** | ||
* By default this plugin ignores legacy elements that were deprecated or | ||
* removed in SVG 2. Set to true to force performing operations on those | ||
* too. | ||
* | ||
* @default false | ||
*/ | ||
includeLegacy: boolean | ||
}; | ||
removeXMLNS: void; | ||
@@ -247,0 +258,0 @@ reusePaths: void; |
@@ -72,3 +72,3 @@ 'use strict'; | ||
* | ||
* @link https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors|MDN CSS Selectors | ||
* @link https://developer.mozilla.org/docs/Web/CSS/CSS_Selectors|MDN CSS Selectors | ||
* | ||
@@ -75,0 +75,0 @@ * @author Bradley Mease |
@@ -15,3 +15,3 @@ 'use strict'; | ||
* | ||
* https://developer.mozilla.org/en-US/docs/Web/SVG/Element/desc | ||
* https://developer.mozilla.org/docs/Web/SVG/Element/desc | ||
* | ||
@@ -18,0 +18,0 @@ * @author Daniel Wabyick |
@@ -33,3 +33,3 @@ 'use strict'; | ||
enter: (node, parentNode) => { | ||
// collect namespace aliases from svg element | ||
// collect namespace prefixes from svg element | ||
if (node.name === 'svg') { | ||
@@ -36,0 +36,0 @@ for (const [name, value] of Object.entries(node.attributes)) { |
@@ -11,3 +11,3 @@ 'use strict'; | ||
* | ||
* https://developer.mozilla.org/en-US/docs/Web/SVG/Element/title | ||
* https://developer.mozilla.org/docs/Web/SVG/Element/title | ||
* | ||
@@ -14,0 +14,0 @@ * @author Igor Kalashnikov |
@@ -25,3 +25,2 @@ 'use strict'; | ||
delete node.attributes.xmlns; | ||
delete node.attributes['xmlns:xlink']; | ||
} | ||
@@ -28,0 +27,0 @@ }, |
@@ -39,3 +39,3 @@ 'use strict'; | ||
* @type {XastElement} | ||
* @see https://developer.mozilla.org/en-US/docs/Web/SVG/Element/defs | ||
* @see https://developer.mozilla.org/docs/Web/SVG/Element/defs | ||
*/ | ||
@@ -42,0 +42,0 @@ let svgDefs; |
207
README.md
<div align="center"> | ||
<img src="./logo/logo-web.svg" width="348.61" height="100" alt="SVGO logo"/> | ||
<img src="./logo/logo-web.svg" width="348.61" height="100" alt=""/> | ||
</div> | ||
## SVGO [![npm version](https://img.shields.io/npm/v/svgo)](https://npmjs.org/package/svgo) [![Discord](https://img.shields.io/discord/815166721315831868)](https://discord.gg/z8jX8NYxrE) | ||
# SVGO [![npm](https://img.shields.io/npm/v/svgo)](https://npmjs.org/package/svgo) [![chat](https://img.shields.io/discord/815166721315831868)](https://discord.gg/z8jX8NYxrE) [![docs](https://img.shields.io/badge/docs-svgo.dev-blue)](https://svgo.dev/) | ||
**SVG O**ptimizer is a Node.js-based tool for optimizing SVG vector graphics files. | ||
SVGO, short for **SVG O**ptimizer, is a Node.js library and command-line application for optimizing SVG files. | ||
## Why? | ||
SVG files, especially those exported from various editors, usually contain a lot of redundant and useless information. This can include editor metadata, comments, hidden elements, default or non-optimal values and other stuff that can be safely removed or converted without affecting the SVG rendering result. | ||
SVG files, especially those exported from vector editors, usually contain a lot of redundant information. This includes editor metadata, comments, hidden elements, default or suboptimal values, and other stuff that can be safely removed or converted without impacting rendering. | ||
## Installation | ||
Via npm: | ||
You can install SVGO globablly through npm, yarn, or pnpm. Alternatively, drop the global flag (`global`/`-g`) to use it in your Node.js project. | ||
```sh | ||
npm -g install svgo | ||
``` | ||
Via yarn: | ||
```sh | ||
# npm | ||
npm install -g svgo | ||
# yarn | ||
yarn global add svgo | ||
# pnpm | ||
pnpm add -g svgo | ||
``` | ||
## CLI usage | ||
## Command-line usage | ||
Processing single files: | ||
Process single files: | ||
```sh | ||
svgo one.svg two.svg -o one.min.svg two.min.svg | ||
``` | ||
Processing directory of svg files, recursively using `-f`, `--folder`: | ||
Process a directory of files recursively with `-f`/`--folder`: | ||
```sh | ||
svgo -f ./path/to/folder/with/svg/files -o ./path/to/folder/with/svg/output | ||
svgo -f path/to/directory_with_svgs -o path/to/output_directory | ||
``` | ||
Help for advanced usage: | ||
```sh | ||
@@ -41,37 +49,34 @@ svgo --help | ||
SVGO has a plugin-based architecture, separate plugins allows various xml svg optimizations. See [built-in plugins](#built-in-plugins). | ||
SVGO automatically loads configuration from `svgo.config.js` or from `--config ./path/myconfig.js`. Some general options can be configured via CLI. | ||
SVGO has a plugin architecture. You can read more about all plugins in [Plugins | SVGO Documentation](https://svgo.dev/docs/plugins/), and the default plugins in [Preset Default | SVGO Documentation](https://svgo.dev/docs/preset-default/). | ||
SVGO reads the configuration from `svgo.config.js` or the `--config path/to/config.js` command-line option. Some other parameters can be configured though command-line options too. | ||
**`svgo.config.js`** | ||
```js | ||
// svgo.config.js | ||
module.exports = { | ||
multipass: true, // boolean. false by default | ||
datauri: 'enc', // 'base64' (default), 'enc' or 'unenc'. | ||
multipass: false, // boolean | ||
datauri: 'base64', // 'base64'|'enc'|'unenc' | ||
js2svg: { | ||
indent: 2, // string with spaces or number of spaces. 4 by default | ||
pretty: true, // boolean, false by default | ||
indent: 4, // number | ||
pretty: false // boolean | ||
}, | ||
plugins: [ | ||
// set of built-in plugins enabled by default | ||
'preset-default', | ||
'preset-default', // built-in plugins enabled by default | ||
'prefixIds', // enable built-in plugins by name | ||
// enable built-in plugins by name | ||
'prefixIds', | ||
// or by expanded notation which allows to configure plugin | ||
// enable built-in plugins with an object to configure plugins | ||
{ | ||
name: 'sortAttrs', | ||
name: 'prefixIds', | ||
params: { | ||
xmlnsOrder: 'alphabetical', | ||
}, | ||
}, | ||
], | ||
prefix: 'uwu' | ||
} | ||
} | ||
] | ||
}; | ||
``` | ||
### Default preset | ||
When extending default configuration specify `preset-default` plugin to enable optimisations. | ||
Each plugin of default preset can be disabled or configured with "overrides" param. | ||
Instead of configuring SVGO from scratch, you can tweak the default preset to suit your needs by configuring or disabling the respective plugin. | ||
**`svgo.config.js`** | ||
```js | ||
@@ -84,35 +89,40 @@ module.exports = { | ||
overrides: { | ||
// customize default plugin options | ||
// disable a default plugin | ||
removeViewBox: false, | ||
// customize the params of a default plugin | ||
inlineStyles: { | ||
onlyMatchedOnce: false, | ||
}, | ||
// or disable plugins | ||
removeDoctype: false, | ||
}, | ||
}, | ||
}, | ||
], | ||
} | ||
} | ||
} | ||
} | ||
] | ||
}; | ||
``` | ||
The default preset includes plugins marked with 'Yes' in the [plugin list](#built-in-plugins) below. | ||
You can find a list of the default plugins in the order they run in [Preset Default | SVGO Documentation](https://svgo.dev/docs/preset-default/#plugins-list). | ||
### Custom plugin | ||
### Custom plugins | ||
It's also possible to specify a custom plugin: | ||
You can also specify custom plugins: | ||
**`svgo.config.js`** | ||
```js | ||
const anotherCustomPlugin = require('./another-custom-plugin.js'); | ||
const importedPlugin = require('./imported-plugin'); | ||
module.exports = { | ||
plugins: [ | ||
// plugin imported from another JavaScript file | ||
importedPlugin, | ||
// plugin defined inline | ||
{ | ||
name: 'customPluginName', | ||
name: 'customPlugin', | ||
params: { | ||
optionName: 'optionValue', | ||
paramName: 'paramValue', | ||
}, | ||
fn: (ast, params, info) => {}, | ||
}, | ||
anotherCustomPlugin, | ||
], | ||
fn: (ast, params, info) => {} | ||
} | ||
] | ||
}; | ||
@@ -131,8 +141,8 @@ ``` | ||
const { optimize } = require('svgo'); | ||
const result = optimize(svgString, { | ||
// optional but recommended field | ||
path: 'path-to.svg', | ||
// all config fields are also available here | ||
multipass: true, | ||
path: 'path-to.svg', // recommended | ||
multipass: true // all other config fields are available here | ||
}); | ||
const optimizedSvgString = result.data; | ||
@@ -143,10 +153,12 @@ ``` | ||
If you write a tool on top of SVGO you might need a way to load SVGO config. | ||
If you write a tool on top of SVGO you may want to resolve the `svgo.config.js` file. | ||
```js | ||
const { loadConfig } = require('svgo'); | ||
const config = await loadConfig(); | ||
``` | ||
You can also specify a relative or absolute path and customize the current working directory. | ||
You can also specify a path and customize the current working directory. | ||
```js | ||
@@ -156,84 +168,15 @@ const config = await loadConfig(configFile, cwd); | ||
## Troubleshooting | ||
### SVG won't scale when CSS is applied on it. | ||
**Observed Problem:** I'm using my SVG files on a website. It looks like the rendered SVG doesn't scale when the dimensions are altered using CSS. | ||
**Possible Solution:** Try disabling `removeViewBox` in the configuration. See [issue #1128](https://github.com/svg/svgo/issues/1128) for details and discussion. | ||
## Built-in plugins | ||
| Plugin | Description | Default | | ||
| ----------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | | ||
| [addAttributesToSVGElement](https://github.com/svg/svgo/blob/main/plugins/addAttributesToSVGElement.js) | adds attributes to an outer `<svg>` element | | | ||
| [addClassesToSVGElement](https://github.com/svg/svgo/blob/main/plugins/addClassesToSVGElement.js) | add classnames to an outer `<svg>` element | | | ||
| [cleanupAttrs](https://github.com/svg/svgo/blob/main/plugins/cleanupAttrs.js) | cleanup attributes from newlines, trailing, and repeating spaces | Yes | | ||
| [cleanupEnableBackground](https://github.com/svg/svgo/blob/main/plugins/cleanupEnableBackground.js) | remove or cleanup `enable-background` attribute when possible | Yes | | ||
| [cleanupIds](https://github.com/svg/svgo/blob/main/plugins/cleanupIds.js) | remove unused and minify used IDs | Yes | | ||
| [cleanupListOfValues](https://github.com/svg/svgo/blob/main/plugins/cleanupListOfValues.js) | round numeric values in attributes that take a list of numbers (like `viewBox` or `enable-background`) | | | ||
| [cleanupNumericValues](https://github.com/svg/svgo/blob/main/plugins/cleanupNumericValues.js) | round numeric values to the fixed precision, remove default `px` units | Yes | | ||
| [collapseGroups](https://github.com/svg/svgo/blob/main/plugins/collapseGroups.js) | collapse useless groups | Yes | | ||
| [convertColors](https://github.com/svg/svgo/blob/main/plugins/convertColors.js) | convert colors (from `rgb()` to `#rrggbb`, from `#rrggbb` to `#rgb`) | Yes | | ||
| [convertEllipseToCircle](https://github.com/svg/svgo/blob/main/plugins/convertEllipseToCircle.js) | convert non-eccentric `<ellipse>` to `<circle>` | Yes | | ||
| [convertOneStopGradients](https://github.com/svg/svgo/blob/main/plugins/convertOneStopGradients.js) | converts one-stop (single color) gradients to a plain color | | | ||
| [convertPathData](https://github.com/svg/svgo/blob/main/plugins/convertPathData.js) | convert Path data to relative or absolute (whichever is shorter), convert one segment to another, trim useless delimiters, smart rounding, and much more | Yes | | ||
| [convertShapeToPath](https://github.com/svg/svgo/blob/main/plugins/convertShapeToPath.js) | convert some basic shapes to `<path>` | Yes | | ||
| [convertStyleToAttrs](https://github.com/svg/svgo/blob/main/plugins/convertStyleToAttrs.js) | convert styles into attributes | | | ||
| [convertTransform](https://github.com/svg/svgo/blob/main/plugins/convertTransform.js) | collapse multiple transforms into one, convert matrices to the short aliases, and much more | Yes | | ||
| [inlineStyles](https://github.com/svg/svgo/blob/main/plugins/inlineStyles.js) | move and merge styles from `<style>` elements to element `style` attributes | Yes | | ||
| [mergePaths](https://github.com/svg/svgo/blob/main/plugins/mergePaths.js) | merge multiple Paths into one | Yes | | ||
| [mergeStyles](https://github.com/svg/svgo/blob/main/plugins/mergeStyles.js) | merge multiple style elements into one | Yes | | ||
| [minifyStyles](https://github.com/svg/svgo/blob/main/plugins/minifyStyles.js) | minify `<style>` elements content with [CSSO](https://github.com/css/csso) | Yes | | ||
| [moveElemsAttrsToGroup](https://github.com/svg/svgo/blob/main/plugins/moveElemsAttrsToGroup.js) | move elements' attributes to their enclosing group | Yes | | ||
| [moveGroupAttrsToElems](https://github.com/svg/svgo/blob/main/plugins/moveGroupAttrsToElems.js) | move some group attributes to the contained elements | Yes | | ||
| [prefixIds](https://github.com/svg/svgo/blob/main/plugins/prefixIds.js) | prefix IDs and classes with the SVG filename or an arbitrary string | | | ||
| [removeAttributesBySelector](https://github.com/svg/svgo/blob/main/plugins/removeAttributesBySelector.js) | removes attributes of elements that match a CSS selector | | | ||
| [removeAttrs](https://github.com/svg/svgo/blob/main/plugins/removeAttrs.js) | remove attributes by pattern | | | ||
| [removeComments](https://github.com/svg/svgo/blob/main/plugins/removeComments.js) | remove comments | Yes | | ||
| [removeDesc](https://github.com/svg/svgo/blob/main/plugins/removeDesc.js) | remove `<desc>` | Yes | | ||
| [removeDimensions](https://github.com/svg/svgo/blob/main/plugins/removeDimensions.js) | remove `width`/`height` and add `viewBox` if it's missing (opposite to removeViewBox, disable it first) | | | ||
| [removeDoctype](https://github.com/svg/svgo/blob/main/plugins/removeDoctype.js) | remove `doctype` declaration | Yes | | ||
| [removeEditorsNSData](https://github.com/svg/svgo/blob/main/plugins/removeEditorsNSData.js) | remove editors namespaces, elements, and attributes | Yes | | ||
| [removeElementsByAttr](https://github.com/svg/svgo/blob/main/plugins/removeElementsByAttr.js) | remove arbitrary elements by `ID` or `className` | | | ||
| [removeEmptyAttrs](https://github.com/svg/svgo/blob/main/plugins/removeEmptyAttrs.js) | remove empty attributes | Yes | | ||
| [removeEmptyContainers](https://github.com/svg/svgo/blob/main/plugins/removeEmptyContainers.js) | remove empty Container elements | Yes | | ||
| [removeEmptyText](https://github.com/svg/svgo/blob/main/plugins/removeEmptyText.js) | remove empty Text elements | Yes | | ||
| [removeHiddenElems](https://github.com/svg/svgo/blob/main/plugins/removeHiddenElems.js) | remove hidden elements | Yes | | ||
| [removeMetadata](https://github.com/svg/svgo/blob/main/plugins/removeMetadata.js) | remove `<metadata>` | Yes | | ||
| [removeNonInheritableGroupAttrs](https://github.com/svg/svgo/blob/main/plugins/removeNonInheritableGroupAttrs.js) | remove non-inheritable group's "presentation" attributes | Yes | | ||
| [removeOffCanvasPaths](https://github.com/svg/svgo/blob/main/plugins/removeOffCanvasPaths.js) | removes elements that are drawn outside of the viewbox | | | ||
| [removeRasterImages](https://github.com/svg/svgo/blob/main/plugins/removeRasterImages.js) | remove raster images | | | ||
| [removeScriptElement](https://github.com/svg/svgo/blob/main/plugins/removeScriptElement.js) | remove scripts | | | ||
| [removeStyleElement](https://github.com/svg/svgo/blob/main/plugins/removeStyleElement.js) | remove `<style>` elements | | | ||
| [removeTitle](https://github.com/svg/svgo/blob/main/plugins/removeTitle.js) | remove `<title>` | Yes | | ||
| [removeUnknownsAndDefaults](https://github.com/svg/svgo/blob/main/plugins/removeUnknownsAndDefaults.js) | remove unknown elements content and attributes, remove attributes with default values | Yes | | ||
| [removeUnusedNS](https://github.com/svg/svgo/blob/main/plugins/removeUnusedNS.js) | remove unused namespaces declaration | Yes | | ||
| [removeUselessDefs](https://github.com/svg/svgo/blob/main/plugins/removeUselessDefs.js) | remove elements of `<defs>` without `id` | Yes | | ||
| [removeUselessStrokeAndFill](https://github.com/svg/svgo/blob/main/plugins/removeUselessStrokeAndFill.js) | remove useless `stroke` and `fill` attributes | Yes | | ||
| [removeViewBox](https://github.com/svg/svgo/blob/main/plugins/removeViewBox.js) | remove `viewBox` attribute when possible | Yes | | ||
| [removeXMLNS](https://github.com/svg/svgo/blob/main/plugins/removeXMLNS.js) | removes the `xmlns` attribute (for inline SVG) | | | ||
| [removeXMLProcInst](https://github.com/svg/svgo/blob/main/plugins/removeXMLProcInst.js) | remove XML processing instructions | Yes | | ||
| [reusePaths](https://github.com/svg/svgo/blob/main/plugins/reusePaths.js) | Find duplicated <path> elements and replace them with <use> links | | | ||
| [sortAttrs](https://github.com/svg/svgo/blob/main/plugins/sortAttrs.js) | sort element attributes for epic readability | Yes | | ||
| [sortDefsChildren](https://github.com/svg/svgo/blob/main/plugins/sortDefsChildren.js) | sort children of `<defs>` in order to improve compression | Yes | | ||
## Other ways to use SVGO | ||
| Method | Reference | | ||
| ------ | --------- | | ||
| --- | --- | | ||
| Web app | [SVGOMG](https://jakearchibald.github.io/svgomg/) | | ||
| GitHub Action | [SVGO Action](https://github.com/marketplace/actions/svgo-action) | | ||
| Grunt task | [grunt-svgmin](https://github.com/sindresorhus/grunt-svgmin) | | ||
| Gulp task | [gulp-svgmin](https://github.com/ben-eb/gulp-svgmin) | | ||
| Mimosa module | [mimosa-minify-svg](https://github.com/dbashford/mimosa-minify-svg) | | ||
| OSX Folder Action | [svgo-osx-folder-action](https://github.com/svg/svgo-osx-folder-action) | | ||
| Webpack loader | [image-minimizer-webpack-plugin](https://github.com/webpack-contrib/image-minimizer-webpack-plugin/#optimize-with-svgo) | | ||
| Telegram Bot | [svgo_bot](https://github.com/maksugr/svgo_bot) | | ||
| PostCSS plugin | [postcss-svgo](https://github.com/cssnano/cssnano/tree/master/packages/postcss-svgo) | | ||
| Inkscape plugin | [inkscape-svgo](https://github.com/konsumer/inkscape-svgo) | | ||
| Sketch plugin | [svgo-compressor](https://github.com/BohemianCoding/svgo-compressor) | | ||
| macOS app | [Image Shrinker](https://image-shrinker.com) | | ||
| Rollup plugin | [rollup-plugin-svgo](https://github.com/porsager/rollup-plugin-svgo) | | ||
| VS Code plugin | [vscode-svgo](https://github.com/1000ch/vscode-svgo) | | ||
| Visual Studio Code plugin | [vscode-svgo](https://github.com/1000ch/vscode-svgo) | | ||
| Atom plugin | [atom-svgo](https://github.com/1000ch/atom-svgo) | | ||
@@ -249,4 +192,4 @@ | Sublime plugin | [Sublime-svgo](https://github.com/1000ch/Sublime-svgo) | | ||
| [<img src="https://sheetjs.com/sketch128.png" width="80">](https://sheetjs.com/) | [<img src="https://raw.githubusercontent.com/fontello/fontello/8.0.0/fontello-image.svg" width="80">](https://fontello.com/) | | ||
| :------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------: | | ||
| [SheetJS LLC](https://sheetjs.com/) | [Fontello](https://fontello.com/) | | ||
| :---: | :---: | | ||
| [SheetJS LLC](https://sheetjs.com/) | [Fontello](https://fontello.com/) | | ||
@@ -253,0 +196,0 @@ ## License and Copyright |
Sorry, the diff of this file is too big to display
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
78
15469
7
1031114
7
195
+ Addedcss-what@^6.1.0