Comparing version 1.2.2 to 1.3.0
@@ -0,1 +1,15 @@ | ||
### [ [>](https://github.com/svg/svgo/tree/v1.3.0) ] 1.3.0 / 14.07.2019 | ||
* Custom plugins now can be loaded from external js through `path` plugin param. | ||
* New plugin `convertEllipseToCircle` to convert ellipse with equal radius measures to circle (by @tigt). | ||
* New plugin `sortDefsChildren` for improved compression (by @davidleston). | ||
* SVGO now removes unnecessary spaces after `arcto` path command flags. | ||
* `removeDimensions` plugin now adds `viewBox` if it's missing (by @adipascu). | ||
* Fixed `removeUnusedNS` not counting attributes in `<svg>` tag itself. | ||
* Fixed an issue with incorrect processing multiple images (by @cyberalien). | ||
* Fixed an error with incorrect converting multiple segmented curve to an arc. | ||
* Fixed an error with matrix decomposition in `convertTransform` due to rounding error leading to illegal value. | ||
* Added `force` option for `mergePaths` plugin (by @goyney). | ||
* Added options to `prefixIds` plugin for selectively prefixing IDs and/or classes (by @strarsis). | ||
* Exported config function (by @1000ch). | ||
### [ [>](https://github.com/svg/svgo/tree/v1.2.2) ] 1.2.2 / 16.04.2019 | ||
@@ -2,0 +16,0 @@ * Update js-yaml for Code Injection warning (by @kaungst). |
@@ -84,4 +84,6 @@ 'use strict'; | ||
SVGO.Config = CONFIG; | ||
module.exports = SVGO; | ||
// Offer ES module interop compatibility. | ||
module.exports.default = SVGO; |
@@ -194,2 +194,3 @@ /* jshint quotmark: false */ | ||
config = YAML.safeLoad(configData); | ||
config.__DIR = PATH.dirname(configPath); // will use it to resolve custom plugins defined via path | ||
@@ -196,0 +197,0 @@ if (!config || Array.isArray(config)) { |
'use strict'; | ||
var FS = require('fs'); | ||
var PATH = require('path'); | ||
var yaml = require('js-yaml'); | ||
@@ -26,7 +27,7 @@ | ||
if (Array.isArray(defaults.plugins)) { | ||
defaults.plugins = preparePluginsArray(defaults.plugins); | ||
defaults.plugins = preparePluginsArray(config, defaults.plugins); | ||
} | ||
} else { | ||
defaults = Object.assign({}, yaml.safeLoad(FS.readFileSync(__dirname + '/../../.svgo.yml', 'utf8'))); | ||
defaults.plugins = preparePluginsArray(defaults.plugins || []); | ||
defaults.plugins = preparePluginsArray(config, defaults.plugins || []); | ||
defaults = extendConfig(defaults, config); | ||
@@ -59,6 +60,7 @@ } | ||
* | ||
* @param {Object} config | ||
* @param {Array} plugins input plugins array | ||
* @return {Array} input plugins array of arrays | ||
*/ | ||
function preparePluginsArray(plugins) { | ||
function preparePluginsArray(config, plugins) { | ||
@@ -81,19 +83,8 @@ var plugin, | ||
plugin = Object.assign({}, require('../../plugins/' + key)); | ||
// name: {} | ||
if (typeof item[key] === 'object') { | ||
plugin.params = Object.assign({}, plugin.params || {}, item[key]); | ||
plugin.active = true; | ||
// name: false | ||
} else if (item[key] === false) { | ||
plugin.active = false; | ||
// name: true | ||
} else if (item[key] === true) { | ||
plugin.active = true; | ||
} | ||
plugin.name = key; | ||
plugin = setPluginActiveState( | ||
loadPlugin(config, key, item[key].path), | ||
item, | ||
key | ||
); | ||
plugin.name = key; | ||
} | ||
@@ -104,4 +95,7 @@ | ||
plugin = Object.assign({}, require('../../plugins/' + item)); | ||
plugin = loadPlugin(config, item); | ||
plugin.name = item; | ||
if (typeof plugin.params === 'object') { | ||
plugin.params = Object.assign({}, plugin.params); | ||
} | ||
@@ -137,2 +131,6 @@ } | ||
if (item[key] == null) { | ||
console.error(`Error: '${key}' plugin is misconfigured! Have you padded its content in YML properly?\n`); | ||
} | ||
// custom | ||
@@ -142,2 +140,6 @@ if (typeof item[key] === 'object' && item[key].fn && typeof item[key].fn === 'function') { | ||
// plugin defined via path | ||
} else if (typeof item[key] === 'object' && item[key].path) { | ||
defaults.plugins.push(setPluginActiveState(loadPlugin(config, undefined, item[key].path), item, key)); | ||
} else { | ||
@@ -147,15 +149,3 @@ defaults.plugins.forEach(function(plugin) { | ||
if (plugin.name === key) { | ||
// name: {} | ||
if (typeof item[key] === 'object') { | ||
plugin.params = Object.assign({}, plugin.params || {}, item[key]); | ||
plugin.active = true; | ||
// name: false | ||
} else if (item[key] === false) { | ||
plugin.active = false; | ||
// name: true | ||
} else if (item[key] === true) { | ||
plugin.active = true; | ||
} | ||
plugin = setPluginActiveState(plugin, item, key); | ||
} | ||
@@ -222,1 +212,47 @@ }); | ||
} | ||
/** | ||
* Sets plugin to active or inactive state. | ||
* | ||
* @param {Object} plugin | ||
* @param {Object} item | ||
* @param {Object} key | ||
* @return {Object} plugin | ||
*/ | ||
function setPluginActiveState(plugin, item, key) { | ||
// name: {} | ||
if (typeof item[key] === 'object') { | ||
plugin.params = Object.assign({}, plugin.params || {}, item[key]); | ||
plugin.active = true; | ||
// name: false | ||
} else if (item[key] === false) { | ||
plugin.active = false; | ||
// name: true | ||
} else if (item[key] === true) { | ||
plugin.active = true; | ||
} | ||
return plugin; | ||
} | ||
/** | ||
* Loads default plugin using name or custom plugin defined via path in config. | ||
* | ||
* @param {Object} config | ||
* @param {Object} name | ||
* @param {Object} path | ||
* @return {Object} plugin | ||
*/ | ||
function loadPlugin(config, name, path) { | ||
var plugin; | ||
if (!path) { | ||
plugin = require('../../plugins/' + name); | ||
} else { | ||
plugin = require(PATH.resolve(config.__DIR, path)); | ||
} | ||
return Object.assign({}, plugin); | ||
} |
@@ -13,8 +13,5 @@ 'use strict'; | ||
exports.encodeSVGDatauri = function(str, type) { | ||
var prefix = 'data:image/svg+xml'; | ||
// base64 | ||
if (!type || type === 'base64') { | ||
// base64 | ||
prefix += ';base64,'; | ||
@@ -26,17 +23,10 @@ if (Buffer.from) { | ||
} | ||
// URI encoded | ||
} else if (type === 'enc') { | ||
// URI encoded | ||
str = prefix + ',' + encodeURIComponent(str); | ||
// unencoded | ||
} else if (type === 'unenc') { | ||
// unencoded | ||
str = prefix + ',' + str; | ||
} | ||
return str; | ||
}; | ||
@@ -59,19 +49,12 @@ | ||
// base64 | ||
if (match[2]) { | ||
// base64 | ||
str = new Buffer(data, 'base64').toString('utf8'); | ||
// URI encoded | ||
} else if (data.charAt(0) === '%') { | ||
// URI encoded | ||
str = decodeURIComponent(data); | ||
// unencoded | ||
} else if (data.charAt(0) === '<') { | ||
// unencoded | ||
str = data; | ||
} | ||
return str; | ||
@@ -86,4 +69,14 @@ }; | ||
exports.cleanupOutData = function(data, params) { | ||
/** | ||
* Convert a row of numbers to an optimized string view. | ||
* | ||
* @example | ||
* [0, -1, .5, .5] → "0-1 .5.5" | ||
* | ||
* @param {number[]} data | ||
* @param {Object} params | ||
* @param {string?} command path data instruction | ||
* @return {string} | ||
*/ | ||
exports.cleanupOutData = function(data, params, command) { | ||
var str = '', | ||
@@ -94,3 +87,2 @@ delimiter, | ||
data.forEach(function(item, i) { | ||
// space delimiter by default | ||
@@ -100,4 +92,8 @@ delimiter = ' '; | ||
// no extra space in front of first number | ||
if (i === 0) { | ||
delimiter = ''; | ||
if (i == 0) delimiter = ''; | ||
// no extra space after 'arcto' command flags | ||
if (params.noSpaceAfterFlags && (command == 'A' || command == 'a')) { | ||
var pos = i % 7; | ||
if (pos == 4 || pos == 5) delimiter = ''; | ||
} | ||
@@ -116,2 +112,3 @@ | ||
params.negativeExtraSpace && | ||
delimiter != '' && | ||
(item < 0 || | ||
@@ -123,12 +120,7 @@ (String(item).charCodeAt(0) == 46 && prev % 1 !== 0) | ||
} | ||
// save prev item value | ||
prev = item; | ||
str += delimiter + item; | ||
}); | ||
return str; | ||
}; | ||
@@ -157,5 +149,3 @@ | ||
} | ||
return strNum; | ||
}; | ||
@@ -162,0 +152,0 @@ |
{ | ||
"name": "svgo", | ||
"version": "1.2.2", | ||
"version": "1.3.0", | ||
"description": "Nodejs-based tool for optimizing SVG vector graphics files", | ||
@@ -13,8 +13,7 @@ "keywords": [ | ||
"bugs": { | ||
"url": "https://github.com/svg/svgo/issues", | ||
"email": "kir@soulshine.in" | ||
"url": "https://github.com/svg/svgo/issues" | ||
}, | ||
"author": { | ||
"name": "Kir Belevich", | ||
"email": "kir@soulshine.in", | ||
"email": "kir@belevi.ch", | ||
"url": "https://github.com/deepsweet" | ||
@@ -57,4 +56,3 @@ }, | ||
"css-select-base-adapter": "^0.1.1", | ||
"css-tree": "1.0.0-alpha.28", | ||
"css-url-regex": "^1.1.0", | ||
"css-tree": "1.0.0-alpha.33", | ||
"csso": "^3.5.1", | ||
@@ -70,10 +68,10 @@ "js-yaml": "^3.13.1", | ||
"devDependencies": { | ||
"coveralls": "^3.0.3", | ||
"fs-extra": "~4.0.3", | ||
"coveralls": "^3.0.5", | ||
"fs-extra": "~8.1.0", | ||
"istanbul": "~0.4.5", | ||
"jshint": "~2.9.5", | ||
"mocha": "~4.0.1", | ||
"jshint": "~2.10.2", | ||
"mocha": "~6.1.4", | ||
"mocha-istanbul": "~0.3.0", | ||
"mock-stdin": "~0.3.1", | ||
"should": "~13.1.2" | ||
"should": "~13.2.3" | ||
}, | ||
@@ -80,0 +78,0 @@ "engines": { |
@@ -556,3 +556,7 @@ /* global a2c */ | ||
path.attr('d').value = data.reduce(function(pathString, item) { | ||
return pathString += item.instruction + (item.data ? cleanupOutData(item.data, params) : ''); | ||
var strData = ''; | ||
if (item.data) { | ||
strData = cleanupOutData(item.data, params, item.instruction); | ||
} | ||
return pathString += item.instruction + strData; | ||
}, ''); | ||
@@ -559,0 +563,0 @@ |
@@ -156,3 +156,4 @@ 'use strict'; | ||
} | ||
var rotate = [mth.acos(data[0] / sx, floatPrecision) * ((scaleBefore ? 1 : sy) * data[1] < 0 ? -1 : 1)]; | ||
var angle = Math.min(Math.max(-1, data[0] / sx), 1), | ||
rotate = [mth.acos(angle, floatPrecision) * ((scaleBefore ? 1 : sy) * data[1] < 0 ? -1 : 1)]; | ||
@@ -159,0 +160,0 @@ if (rotate[0]) transforms.push({ name: 'rotate', data: rotate }); |
@@ -26,2 +26,3 @@ 'use strict'; | ||
negativeExtraSpace: true, | ||
noSpaceAfterFlags: true, | ||
forceAbsolutePath: false | ||
@@ -338,8 +339,9 @@ }; | ||
var prevData = prev.instruction == 'a' ? prev.sdata : prev.data; | ||
angle += findArcAngle(prevData, | ||
var prevAngle = findArcAngle(prevData, | ||
{ | ||
center: [prevData[4] + relCenter[0], prevData[5] + relCenter[1]], | ||
center: [prevData[4] + circle.center[0], prevData[5] + circle.center[1]], | ||
radius: circle.radius | ||
} | ||
); | ||
angle += prevAngle; | ||
if (angle > Math.PI) arc.data[3] = 1; | ||
@@ -965,4 +967,8 @@ hasPrev = 1; | ||
return pathData.reduce(function(pathString, item) { | ||
return pathString + item.instruction + (item.data ? cleanupOutData(roundData(item.data.slice()), params) : ''); | ||
var strData = ''; | ||
if (item.data) { | ||
strData = cleanupOutData(roundData(item.data.slice()), params); | ||
} | ||
return pathString + item.instruction + strData; | ||
}, ''); | ||
} |
@@ -11,4 +11,6 @@ 'use strict'; | ||
collapseRepeated: true, | ||
force: false, | ||
leadingZero: true, | ||
negativeExtraSpace: true | ||
negativeExtraSpace: true, | ||
noSpaceAfterFlags: true | ||
}; | ||
@@ -60,3 +62,3 @@ | ||
if (equalData && !intersects(prevPathJS, curPathJS)) { | ||
if (equalData && (params.force || !intersects(prevPathJS, curPathJS))) { | ||
js2path(prevContentItem, prevPathJS.concat(curPathJS), params); | ||
@@ -63,0 +65,0 @@ return false; |
@@ -8,3 +8,5 @@ 'use strict'; | ||
exports.params = { | ||
delim: '__' | ||
delim: '__', | ||
prefixIds: true, | ||
prefixClassNames: true, | ||
}; | ||
@@ -17,3 +19,2 @@ | ||
csstree = require('css-tree'), | ||
cssRx = require('css-url-regex'), | ||
unquote = require('unquote'), | ||
@@ -42,3 +43,3 @@ collections = require('./_collections.js'), | ||
var matchUrl = function(val) { | ||
var urlMatches = cssRx().exec(val); | ||
var urlMatches = /url\((.*?)\)/gi.exec(val); | ||
if (urlMatches === null) { | ||
@@ -180,4 +181,4 @@ return false; | ||
// #ID, .class | ||
if ((node.type === 'IdSelector' || | ||
node.type === 'ClassSelector') && | ||
if (((opts.prefixIds && node.type === 'IdSelector') || | ||
(opts.prefixClassNames && node.type === 'ClassSelector')) && | ||
node.name) { | ||
@@ -212,8 +213,18 @@ node.name = addPrefix(node.name); | ||
// ID | ||
addPrefixToIdAttr(node.attrs.id); | ||
// Class | ||
addPrefixToClassAttr(node.attrs.class); | ||
// Nodes | ||
if(opts.prefixIds) { | ||
// ID | ||
addPrefixToIdAttr(node.attrs.id); | ||
} | ||
if(opts.prefixClassNames) { | ||
// Class | ||
addPrefixToClassAttr(node.attrs.class); | ||
} | ||
// References | ||
// href | ||
@@ -225,3 +236,3 @@ addPrefixToHrefAttr(node.attrs.href); | ||
// referenceable properties | ||
// (referenceable) properties | ||
for (var referencesProp of referencesProps) { | ||
@@ -228,0 +239,0 @@ addPrefixToUrlAttr(node.attrs[referencesProp]); |
@@ -10,8 +10,8 @@ 'use strict'; | ||
/** | ||
* Remove width/height attributes when a viewBox attribute is present. | ||
* Remove width/height attributes and add the viewBox attribute if it's missing | ||
* | ||
* @example | ||
* <svg width="100" height="50" viewBox="0 0 100 50"> | ||
* <svg width="100" height="50" /> | ||
* ↓ | ||
* <svg viewBox="0 0 100 50"> | ||
* <svg viewBox="0 0 100 50" /> | ||
* | ||
@@ -25,10 +25,27 @@ * @param {Object} item current iteration item | ||
if ( | ||
item.isElem('svg') && | ||
item.hasAttr('viewBox') | ||
) { | ||
item.removeAttr('width'); | ||
item.removeAttr('height'); | ||
if (item.isElem('svg')) { | ||
if (item.hasAttr('viewBox')) { | ||
item.removeAttr('width'); | ||
item.removeAttr('height'); | ||
} else if ( | ||
item.hasAttr('width') && | ||
item.hasAttr('height') && | ||
!isNaN(Number(item.attr('width').value)) && | ||
!isNaN(Number(item.attr('height').value)) | ||
) { | ||
item.addAttr({ | ||
name: 'viewBox', | ||
value: | ||
'0 0 ' + | ||
Number(item.attr('width').value) + | ||
' ' + | ||
Number(item.attr('height').value), | ||
prefix: '', | ||
local: 'viewBox' | ||
}); | ||
item.removeAttr('width'); | ||
item.removeAttr('height'); | ||
} | ||
} | ||
}; |
@@ -69,4 +69,6 @@ 'use strict'; | ||
} else if (xmlnsCollection.length) { | ||
} | ||
if (xmlnsCollection.length) { | ||
// check item for the ns-attrs | ||
@@ -73,0 +75,0 @@ if (item.prefix) { |
@@ -59,4 +59,6 @@ **english** | [русский](https://github.com/svg/svgo/blob/master/README.ru.md) | ||
| [convertShapeToPath](https://github.com/svg/svgo/blob/master/plugins/convertShapeToPath.js) | convert some basic shapes to `<path>` | | ||
| [convertEllipseToCircle](https://github.com/svg/svgo/blob/master/plugins/convertEllipseToCircle.js) | convert non-eccentric `<ellipse>` to `<circle>` | | ||
| [sortAttrs](https://github.com/svg/svgo/blob/master/plugins/sortAttrs.js) | sort element attributes for epic readability (disabled by default) | | ||
| [removeDimensions](https://github.com/svg/svgo/blob/master/plugins/removeDimensions.js) | remove `width`/`height` attributes if `viewBox` is present (opposite to removeViewBox, disable it first) (disabled by default) | | ||
| [sortDefsChildren](https://github.com/svg/svgo/blob/master/plugins/sortDefsChildren.js) | sort children of `<defs>` in order to improve compression | | ||
| [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 by default) | | ||
| [removeAttrs](https://github.com/svg/svgo/blob/master/plugins/removeAttrs.js) | remove attributes by pattern (disabled by default) | | ||
@@ -101,3 +103,3 @@ | [removeAttributesBySelector](https://github.com/svg/svgo/blob/master/plugins/removeAttributesBySelector.js) | removes attributes of elements that match a css selector (disabled by default) | | ||
--datauri=DATAURI : Output as Data URI string (base64, URI encoded or unencoded) | ||
--multipass : Pass over SVGs multiple times to ensure all optimizations are applied | ||
--multipass : Pass over SVGs multiple times to ensure all optimizations are applied | ||
--pretty : Make SVG pretty printed | ||
@@ -201,2 +203,12 @@ --indent=INDENT : Indent number when pretty printing SVGs | ||
## Backers | ||
| [<img src="https://sheetjs.com/sketch128.png" width="80">](https://sheetjs.com/) | [<img src="https://rawgithub.com/fontello/fontello/master/fontello-image.svg" width="80">](http://fontello.com/) | | ||
|:-:|:-:| | ||
| [SheetJS LLC](https://sheetjs.com/) | [Fontello](http://fontello.com/) | | ||
## Donations | ||
- PayPal: https://www.paypal.me/deepsweet | ||
## License and Copyright | ||
@@ -203,0 +215,0 @@ |
@@ -59,3 +59,5 @@ [english](https://github.com/svg/svgo/blob/master/README.md) | **русский** | ||
| [convertShapeToPath](https://github.com/svg/svgo/blob/master/plugins/convertShapeToPath.js) | конвертирование простых форм в Path | | ||
| [convertEllipseToCircle](https://github.com/svg/svgo/blob/master/plugins/convertEllipseToCircle.js) | конвертирование вырожденного эллипса `<ellipse>` в круг `<circle>` | | ||
| [sortAttrs](https://github.com/svg/svgo/blob/master/plugins/sortAttrs.js) | сортировка атрибутов элементов для удобочитаемости (выключено по умолчанию) | | ||
| [sortDefsChildren](https://github.com/svg/svgo/blob/master/plugins/sortDefsChildren.js) | сортировка детей `<defs>` для лучшей компрессии | | ||
| [removeDimensions](https://github.com/svg/svgo/blob/master/plugins/removeDimensions.js) | удаляет атрибуты width/height при наличии viewBox (противоречит removeViewBox — плагин должен быть выключен) (выключено по умолчанию) | | ||
@@ -62,0 +64,0 @@ | [removeAttrs](https://github.com/svg/svgo/blob/master/plugins/removeAttrs.js) | удаляет атрибуты по указанному паттерну (выключено по умолчанию) | |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
404330
13
72
10367
216
+ Addedcss-tree@1.0.0-alpha.33(transitive)
+ Addedmdn-data@2.0.4(transitive)
- Removedcss-url-regex@^1.1.0
- Removedcss-tree@1.0.0-alpha.28(transitive)
- Removedcss-url-regex@1.1.0(transitive)
- Removedmdn-data@1.1.4(transitive)
Updatedcss-tree@1.0.0-alpha.33