Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

svgo

Package Overview
Dependencies
Maintainers
3
Versions
104
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

svgo - npm Package Compare versions

Comparing version 2.3.1 to 2.4.0

plugins/preset-default.js

3

lib/path.js

@@ -281,3 +281,4 @@ 'use strict';

(command === 'A' || command === 'a') &&
(i === 4 || i === 5)
// consider combined arcs
(i % 7 === 4 || i % 7 === 5)
) {

@@ -284,0 +285,0 @@ result += numberString;

@@ -6,4 +6,3 @@ 'use strict';

const specificity = require('csso/lib/restructure/prepare/specificity');
const { selectAll, is } = require('css-select');
const svgoCssSelectAdapter = require('./svgo/css-select-adapter.js');
const { visit, matches } = require('./xast.js');
const { compareSpecificity } = require('./css-tools.js');

@@ -16,7 +15,2 @@ const {

const cssSelectOptions = {
xmlMode: true,
adapter: svgoCssSelectAdapter,
};
const parseRule = (ruleNode, dynamic) => {

@@ -81,3 +75,3 @@ let selectors;

const computeOwnStyle = (node, stylesheet) => {
const computeOwnStyle = (stylesheet, node) => {
const computedStyle = {};

@@ -96,3 +90,3 @@ const importantStyles = new Map();

for (const { selectors, declarations, dynamic } of stylesheet) {
if (is(node, selectors, cssSelectOptions)) {
if (matches(node, selectors)) {
for (const { name, value, important } of declarations) {

@@ -139,29 +133,27 @@ const computed = computedStyle[name];

const computeStyle = (node) => {
// find root
let root = node;
while (root.parentNode) {
root = root.parentNode;
}
// find all styles
const styleNodes = selectAll('style', root, cssSelectOptions);
// parse all styles
const collectStylesheet = (root) => {
const stylesheet = [];
for (const styleNode of styleNodes) {
const dynamic =
styleNode.attributes.media != null &&
styleNode.attributes.media !== 'all';
if (
styleNode.attributes.type == null ||
styleNode.attributes.type === '' ||
styleNode.attributes.type === 'text/css'
) {
const children = styleNode.children;
for (const child of children) {
if (child.type === 'text' || child.type === 'cdata') {
stylesheet.push(...parseStylesheet(child.value, dynamic));
// find and parse all styles
visit(root, {
element: {
enter: (node) => {
if (node.name === 'style') {
const dynamic =
node.attributes.media != null && node.attributes.media !== 'all';
if (
node.attributes.type == null ||
node.attributes.type === '' ||
node.attributes.type === 'text/css'
) {
const children = node.children;
for (const child of children) {
if (child.type === 'text' || child.type === 'cdata') {
stylesheet.push(...parseStylesheet(child.value, dynamic));
}
}
}
}
}
}
}
},
},
});
// sort by selectors specificity

@@ -171,8 +163,12 @@ stable.inplace(stylesheet, (a, b) =>

);
return stylesheet;
};
exports.collectStylesheet = collectStylesheet;
const computeStyle = (stylesheet, node) => {
// collect inherited styles
const computedStyles = computeOwnStyle(node, stylesheet);
const computedStyles = computeOwnStyle(stylesheet, node);
let parent = node;
while (parent.parentNode && parent.parentNode.type !== 'root') {
const inheritedStyles = computeOwnStyle(parent.parentNode, stylesheet);
const inheritedStyles = computeOwnStyle(stylesheet, parent.parentNode);
for (const [name, computed] of Object.entries(inheritedStyles)) {

@@ -179,0 +175,0 @@ if (

@@ -10,3 +10,3 @@ 'use strict';

const js2svg = require('./svgo/js2svg.js');
const invokePlugins = require('./svgo/plugins.js');
const { invokePlugins } = require('./svgo/plugins.js');
const JSAPI = require('./svgo/jsAPI.js');

@@ -46,6 +46,8 @@ const { encodeSVGDatauri } = require('./svgo/tools.js');

}
const resolvedPlugins = plugins.map((plugin) =>
resolvePluginConfig(plugin, config)
);
svgjs = invokePlugins(svgjs, info, resolvedPlugins);
const resolvedPlugins = plugins.map(resolvePluginConfig);
const globalOverrides = {};
if (config.floatPrecision != null) {
globalOverrides.floatPrecision = config.floatPrecision;
}
svgjs = invokePlugins(svgjs, info, resolvedPlugins, null, globalOverrides);
svgjs = js2svg(svgjs, config.js2svg);

@@ -52,0 +54,0 @@ if (svgjs.error) {

@@ -5,3 +5,3 @@ 'use strict';

const PATH = require('path');
const chalk = require('chalk');
const { green } = require('colorette');
const { loadConfig, optimize } = require('../svgo-node.js');

@@ -437,3 +437,3 @@ const pluginsMap = require('../../plugins/plugins.js');

(profitPercents < 0 ? ' + ' : ' - ') +
chalk.green(Math.abs(Math.round(profitPercents * 10) / 10) + '%') +
green(Math.abs(Math.round(profitPercents * 10) / 10) + '%') +
' = ' +

@@ -490,3 +490,3 @@ Math.round((outBytes / 1024) * 1000) / 1000 +

.sort(([a], [b]) => a.localeCompare(b))
.map(([name, plugin]) => ` [ ${chalk.green(name)} ] ${plugin.description}`)
.map(([name, plugin]) => ` [ ${green(name)} ] ${plugin.description}`)
.join('\n');

@@ -493,0 +493,0 @@ console.log('Currently available plugins:\n' + list);

@@ -61,2 +61,20 @@ 'use strict';

const extendDefaultPlugins = (plugins) => {
console.warn(
'\n"extendDefaultPlugins" utility is deprecated.\n' +
'Use "preset-default" plugin with overrides instead.\n' +
'For example:\n' +
`{\n` +
` name: 'preset-default',\n` +
` params: {\n` +
` overrides: {\n` +
` // customize plugin options\n` +
` convertShapeToPath: {\n` +
` convertArcs: true\n` +
` },\n` +
` // disable plugins\n` +
` convertPathData: false\n` +
` }\n` +
` }\n` +
`}\n`
);
const extendedPlugins = pluginsOrder.map((name) => ({

@@ -67,3 +85,3 @@ name,

for (const plugin of plugins) {
const resolvedPlugin = resolvePluginConfig(plugin, {});
const resolvedPlugin = resolvePluginConfig(plugin);
const index = pluginsOrder.indexOf(resolvedPlugin.name);

@@ -80,7 +98,4 @@ if (index === -1) {

const resolvePluginConfig = (plugin, config) => {
const resolvePluginConfig = (plugin) => {
let configParams = {};
if ('floatPrecision' in config) {
configParams.floatPrecision = config.floatPrecision;
}
if (typeof plugin === 'string') {

@@ -87,0 +102,0 @@ // resolve builtin plugin specified as string

@@ -335,3 +335,3 @@ 'use strict';

// newly added class attribute
this.class.hasClass();
this.class.addClassValueHandler();
}

@@ -341,3 +341,3 @@

// newly added style attribute
this.style.hasStyle();
this.style.addStyleValueHandler();
}

@@ -344,0 +344,0 @@

@@ -10,43 +10,36 @@ 'use strict';

*
* @param {Object} data input data
* @param {Object} ast input ast
* @param {Object} info extra information
* @param {Array} plugins plugins object from config
* @return {Object} output data
* @return {Object} output ast
*/
module.exports = function (data, info, plugins) {
// Try to group sequential elements of plugins array
// to optimize ast traversing
const groups = [];
let prev;
const invokePlugins = (ast, info, plugins, overrides, globalOverrides) => {
for (const plugin of plugins) {
if (prev && plugin.type == prev[0].type) {
prev.push(plugin);
} else {
prev = [plugin];
groups.push(prev);
const override = overrides == null ? null : overrides[plugin.name];
if (override === false) {
continue;
}
}
for (const group of groups) {
switch (group[0].type) {
case 'perItem':
data = perItem(data, info, group);
break;
case 'perItemReverse':
data = perItem(data, info, group, true);
break;
case 'full':
data = full(data, info, group);
break;
case 'visitor':
for (const plugin of group) {
if (plugin.active) {
const visitor = plugin.fn(data, plugin.params, info);
visit(data, visitor);
}
}
break;
const params = { ...plugin.params, ...globalOverrides, ...override };
if (plugin.type === 'perItem') {
ast = perItem(ast, info, plugin, params);
}
if (plugin.type === 'perItemReverse') {
ast = perItem(ast, info, plugin, params, true);
}
if (plugin.type === 'full') {
if (plugin.active) {
ast = plugin.fn(ast, params, info);
}
}
if (plugin.type === 'visitor') {
if (plugin.active) {
const visitor = plugin.fn(ast, params, info);
visit(ast, visitor);
}
}
}
return data;
return ast;
};
exports.invokePlugins = invokePlugins;

@@ -62,3 +55,3 @@ /**

*/
function perItem(data, info, plugins, reverse) {
function perItem(data, info, plugin, params, reverse) {
function monkeys(items) {

@@ -70,14 +63,7 @@ items.children = items.children.filter(function (item) {

}
// main filter
var filter = true;
for (var i = 0; filter && i < plugins.length; i++) {
var plugin = plugins[i];
if (plugin.active && plugin.fn(item, plugin.params, info) === false) {
filter = false;
}
let kept = true;
if (plugin.active) {
kept = plugin.fn(item, params, info) !== false;
}
// direct pass

@@ -87,28 +73,23 @@ if (!reverse && item.children) {

}
return filter;
return kept;
});
return items;
}
return monkeys(data);
}
/**
* "Full" plugins.
*
* @param {Object} data input data
* @param {Object} info extra information
* @param {Array} plugins plugins list to process
* @return {Object} output data
*/
function full(data, info, plugins) {
plugins.forEach(function (plugin) {
if (plugin.active) {
data = plugin.fn(data, plugin.params, info);
}
});
return data;
}
const createPreset = ({ name, plugins }) => {
return {
name,
type: 'full',
fn: (ast, params, info) => {
const { floatPrecision, overrides } = params;
const globalOverrides = {};
if (floatPrecision != null) {
globalOverrides.floatPrecision = floatPrecision;
}
return invokePlugins(ast, info, plugins, overrides, globalOverrides);
},
};
};
exports.createPreset = createPreset;

@@ -55,12 +55,2 @@ 'use strict';

/**
* @param {any[]} a
* @param {any[]} b
*/
exports.intersectArrays = function (a, b) {
return a.filter(function (n) {
return b.indexOf(n) > -1;
});
};
/**
* Convert a row of numbers to an optimized string view.

@@ -67,0 +57,0 @@ *

@@ -55,6 +55,6 @@ 'use strict';

const visit = (node, visitor) => {
const visit = (node, visitor, parentNode = null) => {
const callbacks = visitor[node.type];
if (callbacks && callbacks.enter) {
callbacks.enter(node);
callbacks.enter(node, parentNode);
}

@@ -65,3 +65,3 @@ // visit root children

for (const child of node.children) {
visit(child, visitor);
visit(child, visitor, node);
}

@@ -71,5 +71,5 @@ }

if (node.type === 'element') {
if (node.parentNode.children.includes(node)) {
if (parentNode.children.includes(node)) {
for (const child of node.children) {
visit(child, visitor);
visit(child, visitor, node);
}

@@ -79,3 +79,3 @@ }

if (callbacks && callbacks.exit) {
callbacks.exit(node);
callbacks.exit(node, parentNode);
}

@@ -85,4 +85,3 @@ };

const detachNodeFromParent = (node) => {
const parentNode = node.parentNode;
const detachNodeFromParent = (node, parentNode) => {
// avoid splice to not break for loops

@@ -89,0 +88,0 @@ parentNode.children = parentNode.children.filter((child) => child !== node);

{
"name": "svgo",
"version": "2.3.1",
"version": "2.4.0",
"description": "Nodejs-based tool for optimizing SVG vector graphics files",

@@ -49,7 +49,8 @@ "keywords": [

"plugins",
"dist"
"dist",
"!**/**.test.js"
],
"scripts": {
"test": "c8 --reporter=html --reporter=text mocha \"test/*/_index.js\" \"**/*.test.js\" --ignore=\"node_modules/**\"",
"lint": "eslint --ignore-path .gitignore . && prettier --list-different \"**/*.js\" --ignore-path .gitignore",
"test": "jest --coverage",
"lint": "eslint --ignore-path .gitignore . && prettier --check \"**/*.js\" --ignore-path .gitignore",
"fix": "eslint --ignore-path .gitignore --fix . && prettier --write \"**/*.js\" --ignore-path .gitignore",

@@ -86,7 +87,6 @@ "typecheck": "tsc",

"files": [
"test/**/*.js",
"**/*.test.js"
],
"env": {
"mocha": true
"jest": true
}

@@ -98,3 +98,3 @@ }

"@trysound/sax": "0.1.1",
"chalk": "^4.1.0",
"colorette": "^1.2.2",
"commander": "^7.1.0",

@@ -110,8 +110,6 @@ "css-select": "^4.1.3",

"@rollup/plugin-node-resolve": "^11.2.0",
"@types/mocha": "^8.2.2",
"c8": "^7.6.0",
"chai": "^4.3.4",
"@types/jest": "^27.0.0",
"del": "^6.0.0",
"eslint": "^7.22.0",
"mocha": "^8.3.2",
"jest": "^27.0.6",
"mock-stdin": "^1.0.0",

@@ -118,0 +116,0 @@ "node-fetch": "^2.6.1",

@@ -326,2 +326,7 @@ 'use strict';

if (command === 'z' || command === 'Z') {
cursor[0] = start[0];
cursor[1] = start[1];
}
pathItem.instruction = command;

@@ -328,0 +333,0 @@ pathItem.data = args;

@@ -5,2 +5,4 @@ 'use strict';

exports.name = 'addAttributesToSVGElement';
exports.type = 'perItem';

@@ -7,0 +9,0 @@

'use strict';
exports.name = 'addClassesToSVGElement';
exports.type = 'full';

@@ -4,0 +6,0 @@

'use strict';
exports.type = 'perItem';
exports.name = 'cleanupAttrs';
exports.type = 'visitor';
exports.active = true;
exports.description =
'cleanups attributes from newlines, trailing and repeating spaces';
exports.params = {
newlines: true,
trim: true,
spaces: true,
};
const regNewlinesNeedSpace = /(\S)\r?\n(\S)/g;
const regNewlines = /\r?\n/g;
const regSpaces = /\s{2,}/g;
var regNewlinesNeedSpace = /(\S)\r?\n(\S)/g,
regNewlines = /\r?\n/g,
regSpaces = /\s{2,}/g;
/**
* Cleanup attributes values from newlines, trailing and repeating spaces.
*
* @param {Object} item current iteration item
* @param {Object} params plugin params
* @return {Boolean} if false, item will be filtered out
*
* @author Kir Belevich
*/
exports.fn = function (item, params) {
if (item.type === 'element') {
for (const name of Object.keys(item.attributes)) {
if (params.newlines) {
// new line which requires a space instead of themselve
item.attributes[name] = item.attributes[name].replace(
regNewlinesNeedSpace,
(match, p1, p2) => p1 + ' ' + p2
);
// simple new line
item.attributes[name] = item.attributes[name].replace(regNewlines, '');
}
if (params.trim) {
item.attributes[name] = item.attributes[name].trim();
}
if (params.spaces) {
item.attributes[name] = item.attributes[name].replace(regSpaces, ' ');
}
}
}
exports.fn = (root, params) => {
const { newlines = true, trim = true, spaces = true } = params;
return {
element: {
enter: (node) => {
for (const name of Object.keys(node.attributes)) {
if (newlines) {
// new line which requires a space instead of themselve
node.attributes[name] = node.attributes[name].replace(
regNewlinesNeedSpace,
(match, p1, p2) => p1 + ' ' + p2
);
// simple new line
node.attributes[name] = node.attributes[name].replace(
regNewlines,
''
);
}
if (trim) {
node.attributes[name] = node.attributes[name].trim();
}
if (spaces) {
node.attributes[name] = node.attributes[name].replace(
regSpaces,
' '
);
}
}
},
},
};
};

@@ -5,2 +5,4 @@ 'use strict';

exports.name = 'cleanupEnableBackground';
exports.type = 'full';

@@ -7,0 +9,0 @@

@@ -6,2 +6,4 @@ 'use strict';

exports.name = 'cleanupIDs';
exports.type = 'full';

@@ -8,0 +10,0 @@

@@ -5,2 +5,4 @@ 'use strict';

exports.name = 'cleanupListOfValues';
exports.type = 'perItem';

@@ -7,0 +9,0 @@

'use strict';
exports.name = 'cleanupNumericValues';
exports.type = 'perItem';

@@ -4,0 +6,0 @@

@@ -5,2 +5,4 @@ 'use strict';

exports.name = 'collapseGroups';
exports.type = 'perItemReverse';

@@ -7,0 +9,0 @@

'use strict';
exports.name = 'convertColors';
exports.type = 'perItem';

@@ -4,0 +6,0 @@

'use strict';
exports.type = 'perItem';
exports.name = 'convertEllipseToCircle';
exports.type = 'visitor';
exports.active = true;
exports.description = 'converts non-eccentric <ellipse>s to <circle>s';

@@ -14,24 +13,26 @@

*
* @param {Object} item current iteration item
* @return {Boolean} if false, item will be filtered out
*
* @author Taylor Hunt
*/
exports.fn = function (item) {
if (item.isElem('ellipse')) {
const rx = item.attributes.rx || 0;
const ry = item.attributes.ry || 0;
if (
rx === ry ||
rx === 'auto' ||
ry === 'auto' // SVG2
) {
var radius = rx !== 'auto' ? rx : ry;
item.renameElem('circle');
delete item.attributes.rx;
delete item.attributes.ry;
item.attributes.r = radius;
}
}
exports.fn = () => {
return {
element: {
enter: (node) => {
if (node.name === 'ellipse') {
const rx = node.attributes.rx || 0;
const ry = node.attributes.ry || 0;
if (
rx === ry ||
rx === 'auto' ||
ry === 'auto' // SVG2
) {
node.name = 'circle';
const radius = rx === 'auto' ? ry : rx;
delete node.attributes.rx;
delete node.attributes.ry;
node.attributes.r = radius;
}
}
},
},
};
};
'use strict';
const { computeStyle } = require('../lib/style.js');
const { collectStylesheet, computeStyle } = require('../lib/style.js');
const { pathElems } = require('./_collections.js');

@@ -9,2 +9,3 @@ const { path2js, js2path } = require('./_path.js');

exports.name = 'convertPathData';
exports.type = 'visitor';

@@ -59,2 +60,3 @@ exports.active = true;

exports.fn = (root, params) => {
const stylesheet = collectStylesheet(root);
return {

@@ -64,3 +66,3 @@ element: {

if (pathElems.includes(node.name) && node.attributes.d != null) {
const computedStyle = computeStyle(node);
const computedStyle = computeStyle(stylesheet, node);
precision = params.floatPrecision;

@@ -67,0 +69,0 @@ error =

'use strict';
const { stringifyPathData } = require('../lib/path.js');
const { detachNodeFromParent } = require('../lib/xast.js');
exports.type = 'perItem';
exports.name = 'convertShapeToPath';
exports.type = 'visitor';
exports.active = true;
exports.description = 'converts basic shapes to more compact path form';
exports.params = {
convertArcs: false,
floatPrecision: null,
};
const regNumber = /[-+]?(?:\d*\.\d+|\d+\.?)(?:[eE][-+]?\d+)?/g;

@@ -25,122 +20,133 @@

*
* @param {Object} item current iteration item
* @param {Object} params plugin params
* @return {Boolean} if false, item will be filtered out
*
* @author Lev Solntsev
*/
exports.fn = function (item, params) {
const precision = params ? params.floatPrecision : null;
const convertArcs = params && params.convertArcs;
exports.fn = (root, params) => {
const { convertArcs = false, floatPrecision: precision = null } = params;
if (
item.isElem('rect') &&
item.attributes.width != null &&
item.attributes.height != null &&
item.attributes.rx == null &&
item.attributes.ry == null
) {
const x = Number(item.attributes.x || '0');
const y = Number(item.attributes.y || '0');
const width = Number(item.attributes.width);
const height = Number(item.attributes.height);
// Values like '100%' compute to NaN, thus running after
// cleanupNumericValues when 'px' units has already been removed.
// TODO: Calculate sizes from % and non-px units if possible.
if (isNaN(x - y + width - height)) return;
const pathData = [
{ command: 'M', args: [x, y] },
{ command: 'H', args: [x + width] },
{ command: 'V', args: [y + height] },
{ command: 'H', args: [x] },
{ command: 'z', args: [] },
];
item.attributes.d = stringifyPathData({ pathData, precision });
item.renameElem('path');
delete item.attributes.x;
delete item.attributes.y;
delete item.attributes.width;
delete item.attributes.height;
}
return {
element: {
enter: (node, parentNode) => {
// convert rect to path
if (
node.name === 'rect' &&
node.attributes.width != null &&
node.attributes.height != null &&
node.attributes.rx == null &&
node.attributes.ry == null
) {
const x = Number(node.attributes.x || '0');
const y = Number(node.attributes.y || '0');
const width = Number(node.attributes.width);
const height = Number(node.attributes.height);
// Values like '100%' compute to NaN, thus running after
// cleanupNumericValues when 'px' units has already been removed.
// TODO: Calculate sizes from % and non-px units if possible.
if (Number.isNaN(x - y + width - height)) return;
const pathData = [
{ command: 'M', args: [x, y] },
{ command: 'H', args: [x + width] },
{ command: 'V', args: [y + height] },
{ command: 'H', args: [x] },
{ command: 'z', args: [] },
];
node.name = 'path';
node.attributes.d = stringifyPathData({ pathData, precision });
delete node.attributes.x;
delete node.attributes.y;
delete node.attributes.width;
delete node.attributes.height;
}
if (item.isElem('line')) {
const x1 = Number(item.attributes.x1 || '0');
const y1 = Number(item.attributes.y1 || '0');
const x2 = Number(item.attributes.x2 || '0');
const y2 = Number(item.attributes.y2 || '0');
if (isNaN(x1 - y1 + x2 - y2)) return;
const pathData = [
{ command: 'M', args: [x1, y1] },
{ command: 'L', args: [x2, y2] },
];
item.attributes.d = stringifyPathData({ pathData, precision });
item.renameElem('path');
delete item.attributes.x1;
delete item.attributes.y1;
delete item.attributes.x2;
delete item.attributes.y2;
}
// convert line to path
if (node.name === 'line') {
const x1 = Number(node.attributes.x1 || '0');
const y1 = Number(node.attributes.y1 || '0');
const x2 = Number(node.attributes.x2 || '0');
const y2 = Number(node.attributes.y2 || '0');
if (Number.isNaN(x1 - y1 + x2 - y2)) return;
const pathData = [
{ command: 'M', args: [x1, y1] },
{ command: 'L', args: [x2, y2] },
];
node.name = 'path';
node.attributes.d = stringifyPathData({ pathData, precision });
delete node.attributes.x1;
delete node.attributes.y1;
delete node.attributes.x2;
delete node.attributes.y2;
}
if (
(item.isElem('polyline') || item.isElem('polygon')) &&
item.attributes.points != null
) {
const coords = (item.attributes.points.match(regNumber) || []).map(Number);
if (coords.length < 4) return false;
const pathData = [];
for (let i = 0; i < coords.length; i += 2) {
pathData.push({
command: i === 0 ? 'M' : 'L',
args: coords.slice(i, i + 2),
});
}
if (item.isElem('polygon')) {
pathData.push({ command: 'z', args: [] });
}
item.attributes.d = stringifyPathData({ pathData, precision });
item.renameElem('path');
delete item.attributes.points;
}
// convert polyline and polygon to path
if (
(node.name === 'polyline' || node.name === 'polygon') &&
node.attributes.points != null
) {
const coords = (node.attributes.points.match(regNumber) || []).map(
Number
);
if (coords.length < 4) {
detachNodeFromParent(node, parentNode);
return;
}
const pathData = [];
for (let i = 0; i < coords.length; i += 2) {
pathData.push({
command: i === 0 ? 'M' : 'L',
args: coords.slice(i, i + 2),
});
}
if (node.name === 'polygon') {
pathData.push({ command: 'z', args: [] });
}
node.name = 'path';
node.attributes.d = stringifyPathData({ pathData, precision });
delete node.attributes.points;
}
if (item.isElem('circle') && convertArcs) {
const cx = Number(item.attributes.cx || '0');
const cy = Number(item.attributes.cy || '0');
const r = Number(item.attributes.r || '0');
if (isNaN(cx - cy + r)) {
return;
}
const pathData = [
{ command: 'M', args: [cx, cy - r] },
{ command: 'A', args: [r, r, 0, 1, 0, cx, cy + r] },
{ command: 'A', args: [r, r, 0, 1, 0, cx, cy - r] },
{ command: 'z', args: [] },
];
item.attributes.d = stringifyPathData({ pathData, precision });
item.renameElem('path');
delete item.attributes.cx;
delete item.attributes.cy;
delete item.attributes.r;
}
// optionally convert circle
if (node.name === 'circle' && convertArcs) {
const cx = Number(node.attributes.cx || '0');
const cy = Number(node.attributes.cy || '0');
const r = Number(node.attributes.r || '0');
if (Number.isNaN(cx - cy + r)) {
return;
}
const pathData = [
{ command: 'M', args: [cx, cy - r] },
{ command: 'A', args: [r, r, 0, 1, 0, cx, cy + r] },
{ command: 'A', args: [r, r, 0, 1, 0, cx, cy - r] },
{ command: 'z', args: [] },
];
node.name = 'path';
node.attributes.d = stringifyPathData({ pathData, precision });
delete node.attributes.cx;
delete node.attributes.cy;
delete node.attributes.r;
}
if (item.isElem('ellipse') && convertArcs) {
const ecx = Number(item.attributes.cx || '0');
const ecy = Number(item.attributes.cy || '0');
const rx = Number(item.attributes.rx || '0');
const ry = Number(item.attributes.ry || '0');
if (isNaN(ecx - ecy + rx - ry)) {
return;
}
const pathData = [
{ command: 'M', args: [ecx, ecy - ry] },
{ command: 'A', args: [rx, ry, 0, 1, 0, ecx, ecy + ry] },
{ command: 'A', args: [rx, ry, 0, 1, 0, ecx, ecy - ry] },
{ command: 'z', args: [] },
];
item.attributes.d = stringifyPathData({ pathData, precision });
item.renameElem('path');
delete item.attributes.cx;
delete item.attributes.cy;
delete item.attributes.rx;
delete item.attributes.ry;
}
// optionally covert ellipse
if (node.name === 'ellipse' && convertArcs) {
const ecx = Number(node.attributes.cx || '0');
const ecy = Number(node.attributes.cy || '0');
const rx = Number(node.attributes.rx || '0');
const ry = Number(node.attributes.ry || '0');
if (Number.isNaN(ecx - ecy + rx - ry)) {
return;
}
const pathData = [
{ command: 'M', args: [ecx, ecy - ry] },
{ command: 'A', args: [rx, ry, 0, 1, 0, ecx, ecy + ry] },
{ command: 'A', args: [rx, ry, 0, 1, 0, ecx, ecy - ry] },
{ command: 'z', args: [] },
];
node.name = 'path';
node.attributes.d = stringifyPathData({ pathData, precision });
delete node.attributes.cx;
delete node.attributes.cy;
delete node.attributes.rx;
delete node.attributes.ry;
}
},
},
};
};
'use strict';
exports.name = 'convertStyleToAttrs';
exports.type = 'perItem';

@@ -4,0 +6,0 @@

'use strict';
exports.name = 'convertTransform';
exports.type = 'perItem';

@@ -4,0 +6,0 @@

@@ -7,2 +7,4 @@ 'use strict';

exports.name = 'inlineStyles';
exports.type = 'full';

@@ -9,0 +11,0 @@

'use strict';
const { detachNodeFromParent } = require('../lib/xast.js');
const { computeStyle } = require('../lib/style.js');
const { collectStylesheet, computeStyle } = require('../lib/style.js');
const { path2js, js2path, intersects } = require('./_path.js');
exports.name = 'mergePaths';
exports.type = 'visitor';

@@ -25,2 +26,3 @@ exports.active = true;

} = params;
const stylesheet = collectStylesheet(root);

@@ -57,3 +59,3 @@ return {

// preserve paths with markers
const computedStyle = computeStyle(child);
const computedStyle = computeStyle(stylesheet, child);
if (

@@ -92,3 +94,3 @@ computedStyle['marker-start'] ||

});
detachNodeFromParent(child);
detachNodeFromParent(child, node);
continue;

@@ -95,0 +97,0 @@ }

@@ -6,2 +6,3 @@ 'use strict';

exports.name = 'mergeStyles';
exports.type = 'visitor';

@@ -21,3 +22,3 @@ exports.active = true;

const enterElement = (node) => {
const enterElement = (node, parentNode) => {
// collect style elements

@@ -56,3 +57,3 @@ if (node.name !== 'style') {

if (css.trim().length === 0) {
detachNodeFromParent(node);
detachNodeFromParent(node, parentNode);
return;

@@ -73,3 +74,3 @@ }

} else {
detachNodeFromParent(node);
detachNodeFromParent(node, parentNode);
firstStyleElement.children = [

@@ -76,0 +77,0 @@ new JSAPI(

@@ -6,2 +6,4 @@ 'use strict';

exports.name = 'minifyStyles';
exports.type = 'full';

@@ -8,0 +10,0 @@

@@ -5,2 +5,4 @@ 'use strict';

exports.name = 'moveElemsAttrsToGroup';
exports.type = 'perItemReverse';

@@ -7,0 +9,0 @@

@@ -5,2 +5,4 @@ 'use strict';

exports.name = 'moveGroupAttrsToElems';
exports.type = 'perItem';

@@ -7,0 +9,0 @@

'use strict';
// builtin presets
exports['preset-default'] = require('./preset-default.js');
// builtin plugins
exports.addAttributesToSVGElement = require('./addAttributesToSVGElement.js');

@@ -4,0 +8,0 @@ exports.addClassesToSVGElement = require('./addClassesToSVGElement.js');

'use strict';
exports.name = 'prefixIds';
exports.type = 'perItem';

@@ -4,0 +6,0 @@

'use strict';
exports.name = 'removeAttributesBySelector';
exports.type = 'perItem';

@@ -4,0 +6,0 @@

@@ -5,2 +5,4 @@ 'use strict';

exports.name = 'removeAttrs';
exports.type = 'perItem';

@@ -7,0 +9,0 @@

'use strict';
exports.type = 'perItem';
const { detachNodeFromParent } = require('../lib/xast.js');
exports.name = 'removeComments';
exports.type = 'visitor';
exports.active = true;
exports.description = 'removes comments';

@@ -16,11 +17,14 @@

*
* @param {Object} item current iteration item
* @return {Boolean} if false, item will be filtered out
*
* @author Kir Belevich
*/
exports.fn = function (item) {
if (item.type === 'comment' && item.value.charAt(0) !== '!') {
return false;
}
exports.fn = () => {
return {
comment: {
enter: (node, parentNode) => {
if (node.value.charAt(0) !== '!') {
detachNodeFromParent(node, parentNode);
}
},
},
};
};
'use strict';
exports.type = 'perItem';
const { detachNodeFromParent } = require('../lib/xast.js');
exports.name = 'removeDesc';
exports.type = 'visitor';
exports.active = true;
exports.params = {
removeAny: true,
};
exports.description = 'removes <desc>';
var standardDescs = /^(Created with|Created using)/;
const standardDescs = /^(Created with|Created using)/;

@@ -22,17 +19,22 @@ /**

*
* @param {Object} item current iteration item
* @return {Boolean} if false, item will be filtered out
*
* @author Daniel Wabyick
*/
exports.fn = function (item, params) {
return (
!item.isElem('desc') ||
!(
params.removeAny ||
item.children.length === 0 ||
(item.children[0].type === 'text' &&
standardDescs.test(item.children[0].value))
)
);
exports.fn = (root, params) => {
const { removeAny = true } = params;
return {
element: {
enter: (node, parentNode) => {
if (node.name === 'desc') {
if (
removeAny ||
node.children.length === 0 ||
(node.children[0].type === 'text' &&
standardDescs.test(node.children[0].value))
) {
detachNodeFromParent(node, parentNode);
}
}
},
},
};
};
'use strict';
exports.name = 'removeDimensions';
exports.type = 'perItem';

@@ -4,0 +6,0 @@

'use strict';
exports.type = 'perItem';
const { detachNodeFromParent } = require('../lib/xast.js');
exports.name = 'removeDoctype';
exports.type = 'visitor';
exports.active = true;
exports.description = 'removes doctype declaration';

@@ -29,11 +30,12 @@

*
* @param {Object} item current iteration item
* @return {Boolean} if false, item will be filtered out
*
* @author Kir Belevich
*/
exports.fn = function (item) {
if (item.type === 'doctype') {
return false;
}
exports.fn = () => {
return {
doctype: {
enter: (node, parentNode) => {
detachNodeFromParent(node, parentNode);
},
},
};
};

@@ -6,2 +6,4 @@ 'use strict';

exports.name = 'removeEditorsNSData';
exports.type = 'perItem';

@@ -8,0 +10,0 @@

'use strict';
exports.name = 'removeElementsByAttr';
exports.type = 'perItem';

@@ -4,0 +6,0 @@

@@ -5,2 +5,4 @@ 'use strict';

exports.name = 'removeEmptyAttrs';
exports.type = 'perItem';

@@ -7,0 +9,0 @@

@@ -5,2 +5,4 @@ 'use strict';

exports.name = 'removeEmptyContainers';
exports.type = 'perItemReverse';

@@ -7,0 +9,0 @@

'use strict';
exports.type = 'perItem';
const { detachNodeFromParent } = require('../lib/xast.js');
exports.name = 'removeEmptyText';
exports.type = 'visitor';
exports.active = true;
exports.description = 'removes empty <text> elements';
exports.params = {
text: true,
tspan: true,
tref: true,
};
/**

@@ -30,29 +25,28 @@ * Remove empty Text elements.

*
* @param {Object} item current iteration item
* @param {Object} params plugin params
* @return {Boolean} if false, item will be filtered out
*
* @author Kir Belevich
*/
exports.fn = function (item, params) {
if (item.type === 'element') {
// Remove empty text element
if (params.text && item.name === 'text' && item.children.length === 0) {
return false;
}
// Remove empty tspan element
if (params.tspan && item.name === 'tspan' && item.children.length === 0) {
return false;
}
// Remove tref with empty xlink:href attribute
if (
params.tref &&
item.name === 'tref' &&
item.attributes['xlink:href'] == null
) {
return false;
}
}
exports.fn = (root, params) => {
const { text = true, tspan = true, tref = true } = params;
return {
element: {
enter: (node, parentNode) => {
// Remove empty text element
if (text && node.name === 'text' && node.children.length === 0) {
detachNodeFromParent(node, parentNode);
}
// Remove empty tspan element
if (tspan && node.name === 'tspan' && node.children.length === 0) {
detachNodeFromParent(node, parentNode);
}
// Remove tref with empty xlink:href attribute
if (
tref &&
node.name === 'tref' &&
node.attributes['xlink:href'] == null
) {
detachNodeFromParent(node, parentNode);
}
},
},
};
};

@@ -8,5 +8,6 @@ 'use strict';

} = require('../lib/xast.js');
const { computeStyle } = require('../lib/style.js');
const { collectStylesheet, computeStyle } = require('../lib/style.js');
const { parsePathData } = require('../lib/path.js');
exports.name = 'removeHiddenElems';
exports.type = 'visitor';

@@ -53,8 +54,10 @@ exports.active = true;

} = params;
const stylesheet = collectStylesheet(root);
return {
element: {
enter: (node) => {
enter: (node, parentNode) => {
// Removes hidden elements
// https://www.w3schools.com/cssref/pr_class_visibility.asp
const computedStyle = computeStyle(node);
const computedStyle = computeStyle(stylesheet, node);
if (

@@ -68,3 +71,3 @@ isHidden &&

) {
detachNodeFromParent(node);
detachNodeFromParent(node, parentNode);
return;

@@ -86,3 +89,3 @@ }

) {
detachNodeFromParent(node);
detachNodeFromParent(node, parentNode);
return;

@@ -102,3 +105,3 @@ }

) {
detachNodeFromParent(node);
detachNodeFromParent(node, parentNode);
return;

@@ -119,3 +122,3 @@ }

) {
detachNodeFromParent(node);
detachNodeFromParent(node, parentNode);
return;

@@ -136,3 +139,3 @@ }

) {
detachNodeFromParent(node);
detachNodeFromParent(node, parentNode);
return;

@@ -153,3 +156,3 @@ }

) {
detachNodeFromParent(node);
detachNodeFromParent(node, parentNode);
return;

@@ -170,3 +173,3 @@ }

) {
detachNodeFromParent(node);
detachNodeFromParent(node, parentNode);
return;

@@ -188,3 +191,3 @@ }

) {
detachNodeFromParent(node);
detachNodeFromParent(node, parentNode);
return;

@@ -204,3 +207,3 @@ }

) {
detachNodeFromParent(node);
detachNodeFromParent(node, parentNode);
return;

@@ -220,3 +223,3 @@ }

) {
detachNodeFromParent(node);
detachNodeFromParent(node, parentNode);
return;

@@ -236,3 +239,3 @@ }

) {
detachNodeFromParent(node);
detachNodeFromParent(node, parentNode);
return;

@@ -252,3 +255,3 @@ }

) {
detachNodeFromParent(node);
detachNodeFromParent(node, parentNode);
return;

@@ -264,3 +267,3 @@ }

if (node.attributes.d == null) {
detachNodeFromParent(node);
detachNodeFromParent(node, parentNode);
return;

@@ -270,3 +273,3 @@ }

if (pathData.length === 0) {
detachNodeFromParent(node);
detachNodeFromParent(node, parentNode);
return;

@@ -280,3 +283,3 @@ }

) {
detachNodeFromParent(node);
detachNodeFromParent(node, parentNode);
return;

@@ -297,3 +300,3 @@ }

) {
detachNodeFromParent(node);
detachNodeFromParent(node, parentNode);
return;

@@ -312,3 +315,3 @@ }

) {
detachNodeFromParent(node);
detachNodeFromParent(node, parentNode);
return;

@@ -315,0 +318,0 @@ }

'use strict';
exports.type = 'perItem';
const { detachNodeFromParent } = require('../lib/xast.js');
exports.name = 'removeMetadata';
exports.type = 'visitor';
exports.active = true;
exports.description = 'removes <metadata>';

@@ -14,9 +15,14 @@

*
* @param {Object} item current iteration item
* @return {Boolean} if false, item will be filtered out
*
* @author Kir Belevich
*/
exports.fn = function (item) {
return !item.isElem('metadata');
exports.fn = () => {
return {
element: {
enter: (node, parentNode) => {
if (node.name === 'metadata') {
detachNodeFromParent(node, parentNode);
}
},
},
};
};
'use strict';
exports.name = 'removeNonInheritableGroupAttrs';
exports.type = 'perItem';

@@ -4,0 +6,0 @@

'use strict';
exports.name = 'removeOffCanvasPaths';
exports.type = 'perItem';

@@ -4,0 +6,0 @@

'use strict';
exports.type = 'perItem';
const { detachNodeFromParent } = require('../lib/xast.js');
exports.name = 'removeRasterImages';
exports.type = 'visitor';
exports.active = false;
exports.description = 'removes raster images (disabled by default)';

@@ -14,16 +15,18 @@

*
* @param {Object} item current iteration item
* @return {Boolean} if false, item will be filtered out
*
* @author Kir Belevich
*/
exports.fn = function (item) {
if (
item.type === 'element' &&
item.name === 'image' &&
item.attributes['xlink:href'] != null &&
/(\.|image\/)(jpg|png|gif)/.test(item.attributes['xlink:href'])
) {
return false;
}
exports.fn = () => {
return {
element: {
enter: (node, parentNode) => {
if (
node.name === 'image' &&
node.attributes['xlink:href'] != null &&
/(\.|image\/)(jpg|png|gif)/.test(node.attributes['xlink:href'])
) {
detachNodeFromParent(node, parentNode);
}
},
},
};
};
'use strict';
exports.type = 'perItem';
const { detachNodeFromParent } = require('../lib/xast.js');
exports.name = 'removeScriptElement';
exports.type = 'visitor';
exports.active = false;
exports.description = 'removes <script> elements (disabled by default)';

@@ -14,9 +15,15 @@

*
* @param {Object} item current iteration item
* @return {Boolean} if false, item will be filtered out
*
* @author Patrick Klingemann
*/
exports.fn = function (item) {
return !item.isElem('script');
exports.fn = () => {
return {
element: {
enter: (node, parentNode) => {
if (node.name === 'script') {
detachNodeFromParent(node, parentNode);
}
},
},
};
};
'use strict';
exports.type = 'perItem';
const { detachNodeFromParent } = require('../lib/xast.js');
exports.name = 'removeStyleElement';
exports.type = 'visitor';
exports.active = false;
exports.description = 'removes <style> element (disabled by default)';

@@ -14,9 +15,14 @@

*
* @param {Object} item current iteration item
* @return {Boolean} if false, item will be filtered out
*
* @author Betsy Dupuis
*/
exports.fn = function (item) {
return !item.isElem('style');
exports.fn = () => {
return {
element: {
enter: (node, parentNode) => {
if (node.name === 'style') {
detachNodeFromParent(node, parentNode);
}
},
},
};
};
'use strict';
exports.type = 'perItem';
const { detachNodeFromParent } = require('../lib/xast.js');
exports.name = 'removeTitle';
exports.type = 'visitor';
exports.active = true;
exports.description = 'removes <title>';

@@ -14,9 +15,14 @@

*
* @param {Object} item current iteration item
* @return {Boolean} if false, item will be filtered out
*
* @author Igor Kalashnikov
*/
exports.fn = function (item) {
return !item.isElem('title');
exports.fn = () => {
return {
element: {
enter: (node, parentNode) => {
if (node.name === 'title') {
detachNodeFromParent(node, parentNode);
}
},
},
};
};

@@ -5,2 +5,4 @@ 'use strict';

exports.name = 'removeUnknownsAndDefaults';
exports.type = 'perItem';

@@ -7,0 +9,0 @@

@@ -6,2 +6,4 @@ 'use strict';

exports.name = 'removeUnusedNS';
exports.type = 'full';

@@ -8,0 +10,0 @@

@@ -5,2 +5,4 @@ 'use strict';

exports.name = 'removeUselessDefs';
exports.type = 'perItem';

@@ -7,0 +9,0 @@

'use strict';
exports.name = 'removeUselessStrokeAndFill';
exports.type = 'perItem';

@@ -4,0 +6,0 @@

@@ -5,2 +5,4 @@ 'use strict';

exports.name = 'removeViewBox';
exports.type = 'perItem';

@@ -7,0 +9,0 @@

'use strict';
exports.name = 'removeXMLNS';
exports.type = 'perItem';

@@ -4,0 +6,0 @@

'use strict';
exports.type = 'perItem';
const { detachNodeFromParent } = require('../lib/xast.js');
exports.name = 'removeXMLProcInst';
exports.type = 'visitor';
exports.active = true;
exports.description = 'removes XML processing instructions';

@@ -15,12 +16,14 @@

*
* @param {Object} item current iteration item
* @return {Boolean} if false, item will be filtered out
*
* @author Kir Belevich
*/
exports.fn = function (item) {
if (item.type === 'instruction' && item.name === 'xml') {
return false;
}
return true;
exports.fn = () => {
return {
instruction: {
enter: (node, parentNode) => {
if (node.name === 'xml') {
detachNodeFromParent(node, parentNode);
}
},
},
};
};

@@ -6,2 +6,4 @@ 'use strict';

exports.name = 'reusePaths';
exports.type = 'full';

@@ -8,0 +10,0 @@

@@ -5,2 +5,4 @@ 'use strict';

exports.name = 'sortAttrs';
exports.type = 'perItem';

@@ -7,0 +9,0 @@

'use strict';
exports.name = 'sortDefsChildren';
exports.type = 'perItem';

@@ -4,0 +6,0 @@

@@ -5,3 +5,2 @@ <div align="center">

## 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)

@@ -57,4 +56,4 @@

pretty: true, // boolean, false by default
}
}
},
};
```

@@ -72,3 +71,3 @@

{
name: 'builtinPluginName'
name: 'builtinPluginName',
},

@@ -79,87 +78,73 @@ // some plugins allow/require to pass options

params: {
optionName: 'optionValue'
}
}
]
}
optionName: 'optionValue',
},
},
],
};
```
The default list is fully overridden if the `plugins` field is specified. To extend the default
list use the `extendDefaultPlugins` utility:
The default preset of plugins is fully overridden if the `plugins` field is specified.
Use `preset-default` plugin to customize plugins options.
```js
const { extendDefaultPlugins } = require('svgo');
module.exports = {
plugins: extendDefaultPlugins([
plugins: [
{
name: 'builtinPluginName',
name: 'preset-default',
params: {
optionName: 'optionValue'
}
}
])
}
overrides: {
// customize options
builtinPluginName: {
optionName: 'optionValue',
},
// or disable plugins
anotherBuiltinPlugin: false,
},
},
},
],
};
```
To disable one of the default plugins use the `active` field:
Default preset includes the following list of plugins:
```js
const { extendDefaultPlugins } = require('svgo');
module.exports = {
plugins: extendDefaultPlugins([
{
name: 'builtinPluginName',
active: false
}
])
}
```
- removeDoctype
- removeXMLProcInst
- removeComments
- removeMetadata
- removeEditorsNSData
- cleanupAttrs
- mergeStyles
- inlineStyles
- minifyStyles
- cleanupIDs
- removeUselessDefs
- cleanupNumericValues
- convertColors
- removeUnknownsAndDefaults
- removeNonInheritableGroupAttrs
- removeUselessStrokeAndFill
- removeViewBox
- cleanupEnableBackground
- removeHiddenElems
- removeEmptyText
- convertShapeToPath
- convertEllipseToCircle
- moveElemsAttrsToGroup
- moveGroupAttrsToElems
- collapseGroups
- convertPathData
- convertTransform
- removeEmptyAttrs
- removeEmptyContainers
- mergePaths
- removeUnusedNS
- sortDefsChildren
- removeTitle
- removeDesc
See the list of the default plugins:
```js
module.exports = {
plugins: [
'removeDoctype',
'removeXMLProcInst',
'removeComments',
'removeMetadata',
'removeEditorsNSData',
'cleanupAttrs',
'mergeStyles',
'inlineStyles',
'minifyStyles',
'cleanupIDs',
'removeUselessDefs',
'cleanupNumericValues',
'convertColors',
'removeUnknownsAndDefaults',
'removeNonInheritableGroupAttrs',
'removeUselessStrokeAndFill',
'removeViewBox',
'cleanupEnableBackground',
'removeHiddenElems',
'removeEmptyText',
'convertShapeToPath',
'convertEllipseToCircle',
'moveElemsAttrsToGroup',
'moveGroupAttrsToElems',
'collapseGroups',
'convertPathData',
'convertTransform',
'removeEmptyAttrs',
'removeEmptyContainers',
'mergePaths',
'removeUnusedNS',
'sortDefsChildren',
'removeTitle',
'removeDesc'
]
}
```
It's also possible to specify a custom plugin:
```js
const anotherCustomPlugin = require('./another-custom-plugin.js')
const anotherCustomPlugin = require('./another-custom-plugin.js');
module.exports = {

@@ -173,7 +158,7 @@ plugins: [

},
fn: (ast, params, info) => {}
fn: (ast, params, info) => {},
},
anotherCustomPlugin
]
}
anotherCustomPlugin,
],
};
```

@@ -195,5 +180,5 @@

// all config fields are also available here
multipass: true
})
const optimizedSvgString = result.data
multipass: true,
});
const optimizedSvgString = result.data;
```

@@ -207,6 +192,6 @@

const { loadConfig } = require('svgo');
const config = await loadConfig()
const config = await loadConfig();
// you can also specify a relative or absolute path and customize the current working directory
const config = await loadConfig(configFile, cwd)
const config = await loadConfig(configFile, cwd);
```

@@ -216,75 +201,77 @@

| Plugin | Description | Default |
| ------ | ----------- | ------- |
| [cleanupAttrs](https://github.com/svg/svgo/blob/master/plugins/cleanupAttrs.js) | cleanup attributes from newlines, trailing, and repeating spaces | `enabled` |
| [mergeStyles](https://github.com/svg/svgo/blob/master/plugins/mergeStyles.js) | merge multiple style elements into one | `enabled` |
| [inlineStyles](https://github.com/svg/svgo/blob/master/plugins/inlineStyles.js) | move and merge styles from `<style>` elements to element `style` attributes | `enabled` |
| [removeDoctype](https://github.com/svg/svgo/blob/master/plugins/removeDoctype.js) | remove `doctype` declaration | `enabled` |
| [removeXMLProcInst](https://github.com/svg/svgo/blob/master/plugins/removeXMLProcInst.js) | remove XML processing instructions | `enabled` |
| [removeComments](https://github.com/svg/svgo/blob/master/plugins/removeComments.js) | remove comments | `enabled` |
| [removeMetadata](https://github.com/svg/svgo/blob/master/plugins/removeMetadata.js) | remove `<metadata>` | `enabled` |
| [removeTitle](https://github.com/svg/svgo/blob/master/plugins/removeTitle.js) | remove `<title>` | `enabled` |
| [removeDesc](https://github.com/svg/svgo/blob/master/plugins/removeDesc.js) | remove `<desc>` | `enabled` |
| [removeUselessDefs](https://github.com/svg/svgo/blob/master/plugins/removeUselessDefs.js) | remove elements of `<defs>` without `id` | `enabled` |
| [removeXMLNS](https://github.com/svg/svgo/blob/master/plugins/removeXMLNS.js) | removes the `xmlns` attribute (for inline SVG) | `disabled` |
| [removeEditorsNSData](https://github.com/svg/svgo/blob/master/plugins/removeEditorsNSData.js) | remove editors namespaces, elements, and attributes | `enabled` |
| [removeEmptyAttrs](https://github.com/svg/svgo/blob/master/plugins/removeEmptyAttrs.js) | remove empty attributes | `enabled` |
| [removeHiddenElems](https://github.com/svg/svgo/blob/master/plugins/removeHiddenElems.js) | remove hidden elements | `enabled` |
| [removeEmptyText](https://github.com/svg/svgo/blob/master/plugins/removeEmptyText.js) | remove empty Text elements | `enabled` |
| [removeEmptyContainers](https://github.com/svg/svgo/blob/master/plugins/removeEmptyContainers.js) | remove empty Container elements | `enabled` |
| [removeViewBox](https://github.com/svg/svgo/blob/master/plugins/removeViewBox.js) | remove `viewBox` attribute when possible | `enabled` |
| [cleanupEnableBackground](https://github.com/svg/svgo/blob/master/plugins/cleanupEnableBackground.js) | remove or cleanup `enable-background` attribute when possible | `enabled` |
| [minifyStyles](https://github.com/svg/svgo/blob/master/plugins/minifyStyles.js) | minify `<style>` elements content with [CSSO](https://github.com/css/csso) | `enabled` |
| [convertStyleToAttrs](https://github.com/svg/svgo/blob/master/plugins/convertStyleToAttrs.js) | convert styles into attributes | `disabled ` |
| [convertColors](https://github.com/svg/svgo/blob/master/plugins/convertColors.js) | convert colors (from `rgb()` to `#rrggbb`, from `#rrggbb` to `#rgb`) | `enabled` |
| [convertPathData](https://github.com/svg/svgo/blob/master/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 | `enabled` |
| [convertTransform](https://github.com/svg/svgo/blob/master/plugins/convertTransform.js) | collapse multiple transforms into one, convert matrices to the short aliases, and much more | `enabled` |
| [removeUnknownsAndDefaults](https://github.com/svg/svgo/blob/master/plugins/removeUnknownsAndDefaults.js) | remove unknown elements content and attributes, remove attributes with default values | `enabled` |
| [removeNonInheritableGroupAttrs](https://github.com/svg/svgo/blob/master/plugins/removeNonInheritableGroupAttrs.js) | remove non-inheritable group's "presentation" attributes | `enabled` |
| [removeUselessStrokeAndFill](https://github.com/svg/svgo/blob/master/plugins/removeUselessStrokeAndFill.js) | remove useless `stroke` and `fill` attributes | `enabled` |
| [removeUnusedNS](https://github.com/svg/svgo/blob/master/plugins/removeUnusedNS.js) | remove unused namespaces declaration | `enabled` |
| [prefixIds](https://github.com/svg/svgo/blob/master/plugins/prefixIds.js) | prefix IDs and classes with the SVG filename or an arbitrary string | `disabled` |
| [cleanupIDs](https://github.com/svg/svgo/blob/master/plugins/cleanupIDs.js) | remove unused and minify used IDs | `enabled` |
| [cleanupNumericValues](https://github.com/svg/svgo/blob/master/plugins/cleanupNumericValues.js) | round numeric values to the fixed precision, remove default `px` units | `enabled` |
| [cleanupListOfValues](https://github.com/svg/svgo/blob/master/plugins/cleanupListOfValues.js) | round numeric values in attributes that take a list of numbers (like `viewBox` or `enable-background`) | `disabled` |
| [moveElemsAttrsToGroup](https://github.com/svg/svgo/blob/master/plugins/moveElemsAttrsToGroup.js) | move elements' attributes to their enclosing group | `enabled` |
| [moveGroupAttrsToElems](https://github.com/svg/svgo/blob/master/plugins/moveGroupAttrsToElems.js) | move some group attributes to the contained elements | `enabled` |
| [collapseGroups](https://github.com/svg/svgo/blob/master/plugins/collapseGroups.js) | collapse useless groups | `enabled` |
| [removeRasterImages](https://github.com/svg/svgo/blob/master/plugins/removeRasterImages.js) | remove raster images | `disabled` |
| [mergePaths](https://github.com/svg/svgo/blob/master/plugins/mergePaths.js) | merge multiple Paths into one | `enabled` |
| [convertShapeToPath](https://github.com/svg/svgo/blob/master/plugins/convertShapeToPath.js) | convert some basic shapes to `<path>` | `enabled` |
| [convertEllipseToCircle](https://github.com/svg/svgo/blob/master/plugins/convertEllipseToCircle.js) | convert non-eccentric `<ellipse>` to `<circle>` | `enabled` |
| [sortAttrs](https://github.com/svg/svgo/blob/master/plugins/sortAttrs.js) | sort element attributes for epic readability | `disabled` |
| [sortDefsChildren](https://github.com/svg/svgo/blob/master/plugins/sortDefsChildren.js) | sort children of `<defs>` in order to improve compression | `enabled` |
| [removeDimensions](https://github.com/svg/svgo/blob/master/plugins/removeDimensions.js) | remove `width`/`height` and add `viewBox` if it's missing (opposite to removeViewBox, disable it first) | `disabled` |
| [removeAttrs](https://github.com/svg/svgo/blob/master/plugins/removeAttrs.js) | remove attributes by pattern | `disabled` |
| [removeAttributesBySelector](https://github.com/svg/svgo/blob/master/plugins/removeAttributesBySelector.js) | removes attributes of elements that match a CSS selector | `disabled` |
| [removeElementsByAttr](https://github.com/svg/svgo/blob/master/plugins/removeElementsByAttr.js) | remove arbitrary elements by `ID` or `className` | `disabled` |
| [addClassesToSVGElement](https://github.com/svg/svgo/blob/master/plugins/addClassesToSVGElement.js) | add classnames to an outer `<svg>` element | `disabled` |
| [addAttributesToSVGElement](https://github.com/svg/svgo/blob/master/plugins/addAttributesToSVGElement.js) | adds attributes to an outer `<svg>` element | `disabled` |
| [removeOffCanvasPaths](https://github.com/svg/svgo/blob/master/plugins/removeOffCanvasPaths.js) | removes elements that are drawn outside of the viewbox | `disabled` |
| [removeStyleElement](https://github.com/svg/svgo/blob/master/plugins/removeStyleElement.js) | remove `<style>` elements | `disabled` |
| [removeScriptElement](https://github.com/svg/svgo/blob/master/plugins/removeScriptElement.js) | remove `<script>` elements | `disabled` |
| [reusePaths](https://github.com/svg/svgo/blob/master/plugins/reusePaths.js) | Find duplicated <path> elements and replace them with <use> links | `disabled` |
| Plugin | Description | Default |
| ------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- |
| [cleanupAttrs](https://github.com/svg/svgo/blob/master/plugins/cleanupAttrs.js) | cleanup attributes from newlines, trailing, and repeating spaces | `enabled` |
| [mergeStyles](https://github.com/svg/svgo/blob/master/plugins/mergeStyles.js) | merge multiple style elements into one | `enabled` |
| [inlineStyles](https://github.com/svg/svgo/blob/master/plugins/inlineStyles.js) | move and merge styles from `<style>` elements to element `style` attributes | `enabled` |
| [removeDoctype](https://github.com/svg/svgo/blob/master/plugins/removeDoctype.js) | remove `doctype` declaration | `enabled` |
| [removeXMLProcInst](https://github.com/svg/svgo/blob/master/plugins/removeXMLProcInst.js) | remove XML processing instructions | `enabled` |
| [removeComments](https://github.com/svg/svgo/blob/master/plugins/removeComments.js) | remove comments | `enabled` |
| [removeMetadata](https://github.com/svg/svgo/blob/master/plugins/removeMetadata.js) | remove `<metadata>` | `enabled` |
| [removeTitle](https://github.com/svg/svgo/blob/master/plugins/removeTitle.js) | remove `<title>` | `enabled` |
| [removeDesc](https://github.com/svg/svgo/blob/master/plugins/removeDesc.js) | remove `<desc>` | `enabled` |
| [removeUselessDefs](https://github.com/svg/svgo/blob/master/plugins/removeUselessDefs.js) | remove elements of `<defs>` without `id` | `enabled` |
| [removeXMLNS](https://github.com/svg/svgo/blob/master/plugins/removeXMLNS.js) | removes the `xmlns` attribute (for inline SVG) | `disabled` |
| [removeEditorsNSData](https://github.com/svg/svgo/blob/master/plugins/removeEditorsNSData.js) | remove editors namespaces, elements, and attributes | `enabled` |
| [removeEmptyAttrs](https://github.com/svg/svgo/blob/master/plugins/removeEmptyAttrs.js) | remove empty attributes | `enabled` |
| [removeHiddenElems](https://github.com/svg/svgo/blob/master/plugins/removeHiddenElems.js) | remove hidden elements | `enabled` |
| [removeEmptyText](https://github.com/svg/svgo/blob/master/plugins/removeEmptyText.js) | remove empty Text elements | `enabled` |
| [removeEmptyContainers](https://github.com/svg/svgo/blob/master/plugins/removeEmptyContainers.js) | remove empty Container elements | `enabled` |
| [removeViewBox](https://github.com/svg/svgo/blob/master/plugins/removeViewBox.js) | remove `viewBox` attribute when possible | `enabled` |
| [cleanupEnableBackground](https://github.com/svg/svgo/blob/master/plugins/cleanupEnableBackground.js) | remove or cleanup `enable-background` attribute when possible | `enabled` |
| [minifyStyles](https://github.com/svg/svgo/blob/master/plugins/minifyStyles.js) | minify `<style>` elements content with [CSSO](https://github.com/css/csso) | `enabled` |
| [convertStyleToAttrs](https://github.com/svg/svgo/blob/master/plugins/convertStyleToAttrs.js) | convert styles into attributes | `disabled` |
| [convertColors](https://github.com/svg/svgo/blob/master/plugins/convertColors.js) | convert colors (from `rgb()` to `#rrggbb`, from `#rrggbb` to `#rgb`) | `enabled` |
| [convertPathData](https://github.com/svg/svgo/blob/master/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 | `enabled` |
| [convertTransform](https://github.com/svg/svgo/blob/master/plugins/convertTransform.js) | collapse multiple transforms into one, convert matrices to the short aliases, and much more | `enabled` |
| [removeUnknownsAndDefaults](https://github.com/svg/svgo/blob/master/plugins/removeUnknownsAndDefaults.js) | remove unknown elements content and attributes, remove attributes with default values | `enabled` |
| [removeNonInheritableGroupAttrs](https://github.com/svg/svgo/blob/master/plugins/removeNonInheritableGroupAttrs.js) | remove non-inheritable group's "presentation" attributes | `enabled` |
| [removeUselessStrokeAndFill](https://github.com/svg/svgo/blob/master/plugins/removeUselessStrokeAndFill.js) | remove useless `stroke` and `fill` attributes | `enabled` |
| [removeUnusedNS](https://github.com/svg/svgo/blob/master/plugins/removeUnusedNS.js) | remove unused namespaces declaration | `enabled` |
| [prefixIds](https://github.com/svg/svgo/blob/master/plugins/prefixIds.js) | prefix IDs and classes with the SVG filename or an arbitrary string | `disabled` |
| [cleanupIDs](https://github.com/svg/svgo/blob/master/plugins/cleanupIDs.js) | remove unused and minify used IDs | `enabled` |
| [cleanupNumericValues](https://github.com/svg/svgo/blob/master/plugins/cleanupNumericValues.js) | round numeric values to the fixed precision, remove default `px` units | `enabled` |
| [cleanupListOfValues](https://github.com/svg/svgo/blob/master/plugins/cleanupListOfValues.js) | round numeric values in attributes that take a list of numbers (like `viewBox` or `enable-background`) | `disabled` |
| [moveElemsAttrsToGroup](https://github.com/svg/svgo/blob/master/plugins/moveElemsAttrsToGroup.js) | move elements' attributes to their enclosing group | `enabled` |
| [moveGroupAttrsToElems](https://github.com/svg/svgo/blob/master/plugins/moveGroupAttrsToElems.js) | move some group attributes to the contained elements | `enabled` |
| [collapseGroups](https://github.com/svg/svgo/blob/master/plugins/collapseGroups.js) | collapse useless groups | `enabled` |
| [removeRasterImages](https://github.com/svg/svgo/blob/master/plugins/removeRasterImages.js) | remove raster images | `disabled` |
| [mergePaths](https://github.com/svg/svgo/blob/master/plugins/mergePaths.js) | merge multiple Paths into one | `enabled` |
| [convertShapeToPath](https://github.com/svg/svgo/blob/master/plugins/convertShapeToPath.js) | convert some basic shapes to `<path>` | `enabled` |
| [convertEllipseToCircle](https://github.com/svg/svgo/blob/master/plugins/convertEllipseToCircle.js) | convert non-eccentric `<ellipse>` to `<circle>` | `enabled` |
| [sortAttrs](https://github.com/svg/svgo/blob/master/plugins/sortAttrs.js) | sort element attributes for epic readability | `disabled` |
| [sortDefsChildren](https://github.com/svg/svgo/blob/master/plugins/sortDefsChildren.js) | sort children of `<defs>` in order to improve compression | `enabled` |
| [removeDimensions](https://github.com/svg/svgo/blob/master/plugins/removeDimensions.js) | remove `width`/`height` and add `viewBox` if it's missing (opposite to removeViewBox, disable it first) | `disabled` |
| [removeAttrs](https://github.com/svg/svgo/blob/master/plugins/removeAttrs.js) | remove attributes by pattern | `disabled` |
| [removeAttributesBySelector](https://github.com/svg/svgo/blob/master/plugins/removeAttributesBySelector.js) | removes attributes of elements that match a CSS selector | `disabled` |
| [removeElementsByAttr](https://github.com/svg/svgo/blob/master/plugins/removeElementsByAttr.js) | remove arbitrary elements by `ID` or `className` | `disabled` |
| [addClassesToSVGElement](https://github.com/svg/svgo/blob/master/plugins/addClassesToSVGElement.js) | add classnames to an outer `<svg>` element | `disabled` |
| [addAttributesToSVGElement](https://github.com/svg/svgo/blob/master/plugins/addAttributesToSVGElement.js) | adds attributes to an outer `<svg>` element | `disabled` |
| [removeOffCanvasPaths](https://github.com/svg/svgo/blob/master/plugins/removeOffCanvasPaths.js) | removes elements that are drawn outside of the viewbox | `disabled` |
| [removeStyleElement](https://github.com/svg/svgo/blob/master/plugins/removeStyleElement.js) | remove `<style>` elements | `disabled` |
| [removeScriptElement](https://github.com/svg/svgo/blob/master/plugins/removeScriptElement.js) | remove `<script>` elements | `disabled` |
| [reusePaths](https://github.com/svg/svgo/blob/master/plugins/reusePaths.js) | Find duplicated <path> elements and replace them with <use> links | `disabled` |
## Other Ways to Use SVGO
* as a web app – [SVGOMG](https://jakearchibald.github.io/svgomg/)
* as a GitHub Action – [SVGO Action](https://github.com/marketplace/actions/svgo-action)
* as a Node.js module – [examples](https://github.com/svg/svgo/tree/master/examples)
* as a Grunt task – [grunt-svgmin](https://github.com/sindresorhus/grunt-svgmin)
* as a Gulp task – [gulp-svgmin](https://github.com/ben-eb/gulp-svgmin)
* as a Mimosa module – [mimosa-minify-svg](https://github.com/dbashford/mimosa-minify-svg)
* as an OSX Folder Action – [svgo-osx-folder-action](https://github.com/svg/svgo-osx-folder-action)
* as a webpack loader – [image-webpack-loader](https://github.com/tcoopman/image-webpack-loader)
* as a Telegram Bot – [svgo_bot](https://github.com/maksugr/svgo_bot)
* as a PostCSS plugin – [postcss-svgo](https://github.com/ben-eb/postcss-svgo)
* as an Inkscape plugin – [inkscape-svgo](https://github.com/konsumer/inkscape-svgo)
* as a Sketch plugin - [svgo-compressor](https://github.com/BohemianCoding/svgo-compressor)
* as macOS app - [Image Shrinker](https://image-shrinker.com)
* as a Rollup plugin - [rollup-plugin-svgo](https://github.com/porsager/rollup-plugin-svgo)
* as a VS Code plugin - [vscode-svgo](https://github.com/1000ch/vscode-svgo)
* as a Atom plugin - [atom-svgo](https://github.com/1000ch/atom-svgo)
* as a Sublime plugin - [Sublime-svgo](https://github.com/1000ch/Sublime-svgo)
* as a Figma plugin - [Advanced SVG Export](https://www.figma.com/c/plugin/782713260363070260/Advanced-SVG-Export)
- as a web app – [SVGOMG](https://jakearchibald.github.io/svgomg/)
- as a GitHub Action – [SVGO Action](https://github.com/marketplace/actions/svgo-action)
- as a Grunt task – [grunt-svgmin](https://github.com/sindresorhus/grunt-svgmin)
- as a Gulp task – [gulp-svgmin](https://github.com/ben-eb/gulp-svgmin)
- as a Mimosa module – [mimosa-minify-svg](https://github.com/dbashford/mimosa-minify-svg)
- as an OSX Folder Action – [svgo-osx-folder-action](https://github.com/svg/svgo-osx-folder-action)
- as a webpack loader – [image-webpack-loader](https://github.com/tcoopman/image-webpack-loader)
- as a Telegram Bot – [svgo_bot](https://github.com/maksugr/svgo_bot)
- as a PostCSS plugin – [postcss-svgo](https://github.com/ben-eb/postcss-svgo)
- as an Inkscape plugin – [inkscape-svgo](https://github.com/konsumer/inkscape-svgo)
- as a Sketch plugin - [svgo-compressor](https://github.com/BohemianCoding/svgo-compressor)
- as a macOS app - [Image Shrinker](https://image-shrinker.com)
- as a Rollup plugin - [rollup-plugin-svgo](https://github.com/porsager/rollup-plugin-svgo)
- as a VS Code plugin - [vscode-svgo](https://github.com/1000ch/vscode-svgo)
- as a Atom plugin - [atom-svgo](https://github.com/1000ch/atom-svgo)
- as a Sublime plugin - [Sublime-svgo](https://github.com/1000ch/Sublime-svgo)
- as a Figma plugin - [Advanced SVG Export](https://www.figma.com/c/plugin/782713260363070260/Advanced-SVG-Export)
- as a Linux app - [Oh My SVG](https://github.com/sonnyp/OhMySVG)
- as a Browser extension - [SVG Gobbler](https://github.com/rossmoody/svg-gobbler)
- as an API - [Vector Express](https://github.com/smidyo/vectorexpress-api#convertor-svgo)

@@ -294,4 +281,4 @@ ## Donators

| [<img src="https://sheetjs.com/sketch128.png" width="80">](https://sheetjs.com/) | [<img src="https://raw.githubusercontent.com/fontello/fontello/master/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/) |

@@ -298,0 +285,0 @@ ## License and Copyright

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc