svgo
Advanced tools
Comparing version 1.1.1 to 1.2.0
@@ -0,1 +1,13 @@ | ||
### [ [>](https://github.com/svg/svgo/tree/v1.2.0) ] 1.2.0 / 24.02.2019 | ||
Some goodness from pull-requests. | ||
* Fixed extra blank lines when processing many files (by @panczarny). | ||
* Added `--recursive` option to process folders recursevely with option `-f` (by @dartess). | ||
* Added `removeAttributesBySelector` plugin to remove elements matching a css selector (by @bmease). | ||
* Added `removeOffCanvasPaths` plugin to remove elements outside of the viewbox (by @JoshyPHP). | ||
* `removeAttrs` plugin: added `preserveCurrentColor` color (by @roblevintennis) and 3rd optional filter for a value (by @Herman-Freund). | ||
* Added `reusePaths` plugin to replace duplicated elements with link (by @jhowcrof). | ||
* Added support of comma-separated plugins list in `--disable` and `--enable` options (by @jmwebservices). | ||
* Added option to preserve IDs based on prefix in `cleanupIDs` plugin (by @bkotzz). | ||
* Replaced `colors` dependency with `chalk` (by @xPaw). | ||
### [ [>](https://github.com/svg/svgo/tree/v1.1.1) ] 1.1.1 / 17.09.2018 | ||
@@ -2,0 +14,0 @@ * Fixed crash in `SVGO.optimize()` when ‘info’ is absent. |
/* jshint quotmark: false */ | ||
'use strict'; | ||
require('colors'); | ||
var FS = require('fs'), | ||
PATH = require('path'), | ||
chalk = require('chalk'), | ||
mkdirp = require('mkdirp'), | ||
promisify = require('util.promisify'), | ||
@@ -17,2 +17,3 @@ readdir = promisify(FS.readdir), | ||
decodeSVGDatauri = require('./tools.js').decodeSVGDatauri, | ||
checkIsDir = require('./tools.js').checkIsDir, | ||
regSVGFile = /\.svg$/, | ||
@@ -125,2 +126,7 @@ noop = () => {}, | ||
.opt() | ||
.name('recursive').title('Use with \'-f\'. Optimizes *.svg files in folders recursively.') | ||
.short('r').long('recursive') | ||
.flag() | ||
.end() | ||
.opt() | ||
.name('quiet').title('Only output error messages, not regular status messages') | ||
@@ -203,2 +209,7 @@ .short('q').long('quiet') | ||
// --recursive | ||
if (opts.recursive) { | ||
config.recursive = opts.recursive; | ||
} | ||
// --precision | ||
@@ -302,2 +313,4 @@ if (opts.precision) { | ||
function changePluginsState(names, state, config) { | ||
names.forEach(flattenPluginsCbk); | ||
// extend config | ||
@@ -343,2 +356,21 @@ if (config.plugins) { | ||
/** | ||
* Flatten an array of plugins by invoking this callback on each element | ||
* whose value may be a comma separated list of plugins. | ||
* | ||
* @param {String} name Plugin name | ||
* @param {Number} index Plugin index | ||
* @param {Array} names Plugins being traversed | ||
*/ | ||
function flattenPluginsCbk(name, index, names) | ||
{ | ||
var split = name.split(','); | ||
if(split.length > 1) { | ||
names[index] = split.shift(); | ||
names.push.apply(names, split); | ||
} | ||
} | ||
/** | ||
* Optimize SVG files in a directory. | ||
@@ -366,6 +398,7 @@ * @param {Object} config options | ||
function processDirectory(config, dir, files, output) { | ||
// take only *.svg files | ||
var svgFiles = files.filter(name => regSVGFile.test(name)); | ||
return svgFiles.length ? | ||
Promise.all(svgFiles.map(name => optimizeFile(config, PATH.resolve(dir, name), PATH.resolve(output, name)))) : | ||
// take only *.svg files, recursively if necessary | ||
var svgFilesDescriptions = getFilesDescriptions(config, dir, files, output); | ||
return svgFilesDescriptions.length ? | ||
Promise.all(svgFilesDescriptions.map(fileDescription => optimizeFile(config, fileDescription.inputPath, fileDescription.outputPath))) : | ||
Promise.reject(new Error(`No SVG files have been found in '${dir}' directory.`)); | ||
@@ -375,2 +408,34 @@ } | ||
/** | ||
* Get svg files descriptions | ||
* @param {Object} config options | ||
* @param {string} dir input directory | ||
* @param {Array} files list of file names in the directory | ||
* @param {string} output output directory | ||
* @return {Array} | ||
*/ | ||
function getFilesDescriptions(config, dir, files, output) { | ||
const filesInThisFolder = files | ||
.filter(name => regSVGFile.test(name)) | ||
.map(name => ({ | ||
inputPath: PATH.resolve(dir, name), | ||
outputPath: PATH.resolve(output, name), | ||
})); | ||
return config.recursive ? | ||
[].concat( | ||
filesInThisFolder, | ||
files | ||
.filter(name => checkIsDir(PATH.resolve(dir, name))) | ||
.map(subFolderName => { | ||
const subFolderPath = PATH.resolve(dir, subFolderName); | ||
const subFolderFiles = FS.readdirSync(subFolderPath); | ||
const subFolderOutput = PATH.resolve(output, subFolderName); | ||
return getFilesDescriptions(config, subFolderPath, subFolderFiles, subFolderOutput); | ||
}) | ||
.reduce((a, b) => [].concat(a, b), []) | ||
) : | ||
filesInThisFolder; | ||
} | ||
/** | ||
* Read SVG file and pass to processing. | ||
@@ -433,5 +498,9 @@ * @param {Object} config options | ||
} | ||
mkdirp.sync(PATH.dirname(output)); | ||
return writeFile(output, data, 'utf8').catch(error => checkWriteFileError(input, output, data, error)); | ||
} | ||
/** | ||
@@ -456,3 +525,3 @@ * Write a time taken by optimization. | ||
(profitPercents < 0 ? ' + ' : ' - ') + | ||
String(Math.abs((Math.round(profitPercents * 10) / 10)) + '%').green + ' = ' + | ||
chalk.green(Math.abs((Math.round(profitPercents * 10) / 10)) + '%') + ' = ' + | ||
(Math.round((outBytes / 1024) * 1000) / 1000) + ' KiB' | ||
@@ -496,14 +565,2 @@ ); | ||
/** | ||
* Synchronously check if path is a directory. Tolerant to errors like ENOENT. | ||
* @param {string} path | ||
*/ | ||
function checkIsDir(path) { | ||
try { | ||
return FS.lstatSync(path).isDirectory(); | ||
} catch(e) { | ||
return false; | ||
} | ||
} | ||
/** | ||
* Show list of available plugins with short description. | ||
@@ -517,3 +574,3 @@ */ | ||
.sort((a, b) => a.name.localeCompare(b.name)) | ||
.map(plugin => ` [ ${plugin.name.green} ] ${plugin.description}`) | ||
.map(plugin => ` [ ${chalk.green(plugin.name)} ] ${plugin.description}`) | ||
.join('\n'); | ||
@@ -529,5 +586,5 @@ console.log(list); | ||
function printErrorAndExit(error) { | ||
console.error(error); | ||
console.error(chalk.red(error)); | ||
process.exit(1); | ||
return Promise.reject(error); // for tests | ||
} | ||
} |
@@ -60,3 +60,3 @@ 'use strict'; | ||
} else { | ||
this.config = defaults; | ||
this.config = Object.assign({}, defaults); | ||
} | ||
@@ -63,0 +63,0 @@ |
'use strict'; | ||
var FS = require('fs'); | ||
/** | ||
@@ -151,1 +153,14 @@ * Encode plain SVG data string into Data URI string. | ||
}; | ||
/** | ||
* Synchronously check if path is a directory. Tolerant to errors like ENOENT. | ||
* @param {string} path | ||
*/ | ||
exports.checkIsDir = function(path) { | ||
try { | ||
return FS.lstatSync(path).isDirectory(); | ||
} catch(e) { | ||
return false; | ||
} | ||
}; |
{ | ||
"name": "svgo", | ||
"version": "1.1.1", | ||
"version": "1.2.0", | ||
"description": "Nodejs-based tool for optimizing SVG vector graphics files", | ||
@@ -52,14 +52,14 @@ "keywords": [ | ||
"dependencies": { | ||
"coa": "~2.0.1", | ||
"colors": "~1.1.2", | ||
"chalk": "^2.4.1", | ||
"coa": "^2.0.2", | ||
"css-select": "^2.0.0", | ||
"css-select-base-adapter": "~0.1.0", | ||
"css-select-base-adapter": "^0.1.1", | ||
"css-tree": "1.0.0-alpha.28", | ||
"css-url-regex": "^1.1.0", | ||
"csso": "^3.5.0", | ||
"csso": "^3.5.1", | ||
"js-yaml": "^3.12.0", | ||
"mkdirp": "~0.5.1", | ||
"object.values": "^1.0.4", | ||
"object.values": "^1.1.0", | ||
"sax": "~1.2.4", | ||
"stable": "~0.1.6", | ||
"stable": "^0.1.8", | ||
"unquote": "~1.1.1", | ||
@@ -69,3 +69,3 @@ "util.promisify": "~1.0.0" | ||
"devDependencies": { | ||
"coveralls": "~3.0.0", | ||
"coveralls": "^3.0.3", | ||
"fs-extra": "~4.0.3", | ||
@@ -72,0 +72,0 @@ "istanbul": "~0.4.5", |
@@ -2283,3 +2283,10 @@ 'use strict'; | ||
'http://ns.adobe.com/XPath/1.0/', | ||
'http://schemas.microsoft.com/visio/2003/SVGExtensions/' | ||
'http://schemas.microsoft.com/visio/2003/SVGExtensions/', | ||
'http://taptrix.com/vectorillustrator/svg_extensions', | ||
'http://www.figma.com/figma/ns', | ||
'http://purl.org/dc/elements/1.1/', | ||
'http://creativecommons.org/ns#', | ||
'http://www.w3.org/1999/02/22-rdf-syntax-ns#', | ||
'http://www.serif.com/', | ||
'http://www.vector.evaxdesign.sk' | ||
]; | ||
@@ -2286,0 +2293,0 @@ |
@@ -14,2 +14,3 @@ 'use strict'; | ||
preserve: [], | ||
preservePrefixes: [], | ||
force: false | ||
@@ -45,2 +46,3 @@ }; | ||
preserveIDs = new Set(Array.isArray(params.preserve) ? params.preserve : params.preserve ? [params.preserve] : []), | ||
preserveIDPrefixes = new Set(Array.isArray(params.preservePrefixes) ? params.preservePrefixes : (params.preservePrefixes ? [params.preservePrefixes] : [])), | ||
idValuePrefix = '#', | ||
@@ -59,3 +61,3 @@ idValuePostfix = '.'; | ||
// quit if <style> of <script> presents ('force' param prevents quitting) | ||
// quit if <style> or <script> present ('force' param prevents quitting) | ||
if (!params.force) { | ||
@@ -130,3 +132,3 @@ if (item.isElem(styleOrScript)) { | ||
// replace referenced IDs with the minified ones | ||
if (params.minify && !preserveIDs.has(key)) { | ||
if (params.minify && !preserveIDs.has(key) && !idMatchesPrefix(preserveIDPrefixes, key)) { | ||
currentIDstring = getIDstring(currentID = generateID(currentID), params); | ||
@@ -148,3 +150,3 @@ IDs.get(key).attr('id').value = currentIDstring; | ||
for(var keyElem of IDs) { | ||
if (!preserveIDs.has(keyElem[0])) { | ||
if (!preserveIDs.has(keyElem[0]) && !idMatchesPrefix(preserveIDPrefixes, keyElem[0])) { | ||
keyElem[1].removeAttr('id'); | ||
@@ -158,2 +160,16 @@ } | ||
/** | ||
* Check if an ID starts with any one of a list of strings. | ||
* | ||
* @param {Array} of prefix strings | ||
* @param {String} current ID | ||
* @return {Boolean} if currentID starts with one of the strings in prefixArray | ||
*/ | ||
function idMatchesPrefix(prefixArray, currentID) { | ||
if (!currentID) return false; | ||
for (var prefix of prefixArray) if (currentID.startsWith(prefix)) return true; | ||
return false; | ||
} | ||
/** | ||
* Generate unique minimal ID. | ||
@@ -160,0 +176,0 @@ * |
@@ -13,2 +13,3 @@ 'use strict'; | ||
elemSeparator: DEFAULT_SEPARATOR, | ||
preserveCurrentColor: false, | ||
attrs: [] | ||
@@ -23,8 +24,12 @@ }; | ||
* | ||
* @param preserveCurrentColor | ||
* format: boolean | ||
* | ||
* @param attrs: | ||
* | ||
* format: [ element* : attribute* ] | ||
* format: [ element* : attribute* : value* ] | ||
* | ||
* element : regexp (wrapped into ^...$), single * or omitted > all elements | ||
* element : regexp (wrapped into ^...$), single * or omitted > all elements (must be present when value is used) | ||
* attribute : regexp (wrapped into ^...$) | ||
* value : regexp (wrapped into ^...$), single * or omitted > all values | ||
* | ||
@@ -42,3 +47,7 @@ * examples: | ||
* | ||
* > remove fill attribute on path element where value is none | ||
* --- | ||
* attrs: 'path:fill:none' | ||
* | ||
* | ||
* > remove all fill and stroke attribute | ||
@@ -62,3 +71,7 @@ * --- | ||
* | ||
* [is same as] | ||
* | ||
* attrs: '.*:(fill|stroke):.*' | ||
* | ||
* | ||
* > remove all stroke related attributes | ||
@@ -76,3 +89,2 @@ * ---- | ||
exports.fn = function(item, params) { | ||
// wrap into an array if params is not | ||
@@ -85,2 +97,3 @@ if (!Array.isArray(params.attrs)) { | ||
var elemSeparator = typeof params.elemSeparator == 'string' ? params.elemSeparator : DEFAULT_SEPARATOR; | ||
var preserveCurrentColor = typeof params.preserveCurrentColor == 'boolean' ? params.preserveCurrentColor : false; | ||
@@ -90,8 +103,12 @@ // prepare patterns | ||
// apply to all elements if specifc element is omitted | ||
// if no element separators (:), assume it's attribute name, and apply to all elements *regardless of value* | ||
if (pattern.indexOf(elemSeparator) === -1) { | ||
pattern = ['.*', elemSeparator, pattern].join(''); | ||
pattern = ['.*', elemSeparator, pattern, elemSeparator, '.*'].join(''); | ||
// if only 1 separator, assume it's element and attribute name, and apply regardless of attribute value | ||
} else if (pattern.split(elemSeparator).length < 3) { | ||
pattern = [pattern, elemSeparator, '.*'].join(''); | ||
} | ||
// create regexps for element and attribute name | ||
// create regexps for element, attribute name, and attribute value | ||
return pattern.split(elemSeparator) | ||
@@ -117,6 +134,15 @@ .map(function(value) { | ||
var name = attr.name; | ||
var value = attr.value; | ||
var isFillCurrentColor = preserveCurrentColor && name == 'fill' && value == 'currentColor'; | ||
var isStrokeCurrentColor = preserveCurrentColor && name == 'stroke' && value == 'currentColor'; | ||
if (!(isFillCurrentColor || isStrokeCurrentColor)) { | ||
// matches attribute name | ||
if (pattern[1].test(name)) { | ||
item.removeAttr(name); | ||
if (pattern[1].test(name)) { | ||
// matches attribute value | ||
if (pattern[2].test(attr.value)) { | ||
item.removeAttr(name); | ||
} | ||
} | ||
} | ||
@@ -123,0 +149,0 @@ |
@@ -49,2 +49,3 @@ **english** | [русский](https://github.com/svg/svgo/blob/master/README.ru.md) | ||
| [removeUnusedNS](https://github.com/svg/svgo/blob/master/plugins/removeUnusedNS.js) | remove unused namespaces declaration | | ||
| [prefixIds](https://github.com/svg/svgo/blob/master/plugins/prefixIds.js) | prefix IDs and classes with the SVG filename or an arbitrary string | | ||
| [cleanupIDs](https://github.com/svg/svgo/blob/master/plugins/cleanupIDs.js) | remove unused and minify used IDs | | ||
@@ -62,7 +63,10 @@ | [cleanupNumericValues](https://github.com/svg/svgo/blob/master/plugins/cleanupNumericValues.js) | round numeric values to the fixed precision, remove default `px` units | | ||
| [removeAttrs](https://github.com/svg/svgo/blob/master/plugins/removeAttrs.js) | remove attributes by pattern (disabled by default) | | ||
| [removeAttributesBySelector](https://github.com/svg/svgo/blob/master/plugins/removeAttributesBySelector.js) | removes attributes of elements that match a css selector (disabled by default) | | ||
| [removeElementsByAttr](https://github.com/svg/svgo/blob/master/plugins/removeElementsByAttr.js) | remove arbitrary elements by ID or className (disabled by default) | | ||
| [addClassesToSVGElement](https://github.com/svg/svgo/blob/master/plugins/addClassesToSVGElement.js) | add classnames to an outer `<svg>` element (disabled by default) | | ||
| [addAttributesToSVGElement](https://github.com/svg/svgo/blob/master/plugins/addAttributesToSVGElement.js) | adds attributes to an outer `<svg>` element (disabled by default) | | ||
| [removeOffCanvasPaths](https://github.com/svg/svgo/blob/master/plugins/removeOffCanvasPaths.js) | removes elements that are drawn outside of the viewbox (disabled by default) | | ||
| [removeStyleElement](https://github.com/svg/svgo/blob/master/plugins/removeStyleElement.js) | remove `<style>` elements (disabled by default) | | ||
| [removeScriptElement](https://github.com/svg/svgo/blob/master/plugins/removeScriptElement.js) | remove `<script>` elements (disabled by default) | | ||
| [reusePaths](https://github.com/svg/svgo/blob/master/plugins/reusePaths.js) | Find duplicated <path> elements and replace them with <use> links (disabled by default) | | ||
@@ -95,4 +99,4 @@ Want to know how it works and how to write your own plugin? [Of course you want to](https://github.com/svg/svgo/blob/master/docs/how-it-works/en.md). ([동작방법](https://github.com/svg/svgo/blob/master/docs/how-it-works/ko.md)) | ||
--config=CONFIG : Config file or JSON string to extend or replace default | ||
--disable=PLUGIN : Disable plugin by name, "--disable={PLUGIN1,PLUGIN2}" for multiple plugins (*nix) | ||
--enable=PLUGIN : Enable plugin by name, "--enable={PLUGIN3,PLUGIN4}" for multiple plugins (*nix) | ||
--disable=PLUGIN : Disable plugin by name, "--disable=PLUGIN1,PLUGIN2" for multiple plugins | ||
--enable=PLUGIN : Enable plugin by name, "--enable=PLUGIN3,PLUGIN4" for multiple plugins | ||
--datauri=DATAURI : Output as Data URI string (base64, URI encoded or unencoded) | ||
@@ -102,2 +106,3 @@ --multipass : Enable multipass | ||
--indent=INDENT : Indent number when pretty printing SVGs | ||
-r, --recursive : Use with '-f'. Optimizes *.svg files in folders recursively. | ||
-q, --quiet : Only output error messages, not regular status messages | ||
@@ -104,0 +109,0 @@ --show-plugins : Show available plugins and exit |
@@ -49,2 +49,3 @@ [english](https://github.com/svg/svgo/blob/master/README.md) | **русский** | ||
| [removeUnusedNS](https://github.com/svg/svgo/blob/master/plugins/removeUnusedNS.js) | удаление деклараций неиспользуемых пространств имён | | ||
| [prefixIds](https://github.com/svg/svgo/blob/master/plugins/prefixIds.js) | добавляет префикс в ID или классы в виде имени файла или произвольной строки | | ||
| [cleanupIDs](https://github.com/svg/svgo/blob/master/plugins/cleanupIDs.js) | удаление неиспользуемых и сокращение используемых ID | | ||
@@ -56,3 +57,3 @@ | [cleanupNumericValues](https://github.com/svg/svgo/blob/master/plugins/cleanupNumericValues.js) | округление дробных чисел до заданной точности, удаление `px` как единицы |измерения по-умолчанию | ||
| [collapseGroups](https://github.com/svg/svgo/blob/master/plugins/collapseGroups.js) | схлопывание бесполезных групп `<g>` | | ||
| [removeRasterImage](https://github.com/svg/svgo/blob/master/plugins/removeRasterImages.js) | удаление растровых изображений (выключено по умолчанию) | | ||
| [removeRasterImages](https://github.com/svg/svgo/blob/master/plugins/removeRasterImages.js) | удаление растровых изображений (выключено по умолчанию) | | ||
| [mergePaths](https://github.com/svg/svgo/blob/master/plugins/mergePaths.js) | склеивание нескольких Path в одну кривую | | ||
@@ -63,7 +64,10 @@ | [convertShapeToPath](https://github.com/svg/svgo/blob/master/plugins/convertShapeToPath.js) | конвертирование простых форм в Path | | ||
| [removeAttrs](https://github.com/svg/svgo/blob/master/plugins/removeAttrs.js) | удаляет атрибуты по указанному паттерну (выключено по умолчанию) | | ||
| [removeAttributesBySelector](https://github.com/svg/svgo/blob/master/plugins/removeAttributesBySelector.js) | удаляет атрибуты по CSS-селектору (выключено по умолчанию) | | ||
| [removeElementsByAttr](https://github.com/svg/svgo/blob/master/plugins/removeElementsByAttr.js) | удаляет элементы по указанным ID или классам (выключено по умолчанию) | | ||
| [addClassesToSVGElement](https://github.com/svg/svgo/blob/master/plugins/addClassesToSVGElement.js) | добавляет имена классов корневому элементу `<svg>` (выключено по умолчанию) | | ||
| [addAttributesToSVGElement](https://github.com/svg/svgo/blob/master/plugins/addAttributesToSVGElement.js) | добавляет атрибуты корневому элементу `<svg>` (выключено |по умолчанию) | ||
| [removeOffCanvasPaths](https://github.com/svg/svgo/blob/master/plugins/removeOffCanvasPaths.js) | удаляет элементы вне отрисовываемой области (выключено по умолчанию) | | ||
| [removeStyleElement](https://github.com/svg/svgo/blob/master/plugins/removeStyleElement.js) | удаляет элементы `<style>` (выключено по умолчанию) | | ||
| [removeScriptElement](https://github.com/svg/svgo/blob/master/plugins/removeScriptElement.js) | удаляет элементы `<script>` (выключено по умолчанию) | | ||
| [reusePaths](https://github.com/svg/svgo/blob/master/plugins/reusePaths.js) | Заменяет дублирующиеся элементы <path> ссылками <use> (выключено по умолчанию) | | ||
@@ -96,4 +100,4 @@ Хотите узнать принципы работы и как написать свой плагин? [Конечно же, да!](https://github.com/svg/svgo/blob/master/docs/how-it-works/ru.md) | ||
--config=CONFIG : Файл конфигурации (или строка JSON) для расширения и замены настроек | ||
--disable=DISABLE : Выключение плагина по имени | ||
--enable=ENABLE : Включение плагина по имени | ||
--disable=PLUGIN : Выключение плагина по имени, "--disable=PLUGIN1,PLUGIN2" для отключения нескольких плагинов | ||
--enable=PLUGIN : Включение плагина по имени, "--enable=PLUGIN3,PLUGIN4" для отключения нескольких плагинов | ||
--datauri=DATAURI : Результат в виде строки Data URI (base64, URI encoded или unencoded) | ||
@@ -103,2 +107,3 @@ --multipass : Оптимизация в несколько проходов | ||
--indent=INDENT : Размер отступа для удобочитаемого форматирования | ||
-r, --recursive : Совместно с '-f'. Рекурсивно обрабатывать *.svg файлы в папках. | ||
-q, --quiet : Подавляет вывод информации, выводятся только сообщения об ошибках | ||
@@ -105,0 +110,0 @@ --show-plugins : Доступные плагины |
Sorry, the diff of this file is not supported yet
396931
70
10206
204
5
+ Addedchalk@^2.4.1
- Removedcolors@~1.1.2
- Removedcolors@1.1.2(transitive)
Updatedcoa@^2.0.2
Updatedcsso@^3.5.1
Updatedobject.values@^1.1.0
Updatedstable@^0.1.8