Comparing version 1.3.2 to 2.0.0
126
lib/svgo.js
@@ -13,79 +13,69 @@ 'use strict'; | ||
var CONFIG = require('./svgo/config.js'), | ||
SVG2JS = require('./svgo/svg2js.js'), | ||
PLUGINS = require('./svgo/plugins.js'), | ||
JSAPI = require('./svgo/jsAPI.js'), | ||
encodeSVGDatauri = require('./svgo/tools.js').encodeSVGDatauri, | ||
JS2SVG = require('./svgo/js2svg.js'); | ||
const { | ||
defaultPlugins, | ||
resolvePluginConfig, | ||
extendDefaultPlugins | ||
} = require('./svgo/config.js'); | ||
const svg2js = require('./svgo/svg2js.js'); | ||
const js2svg = require('./svgo/js2svg.js'); | ||
const invokePlugins = require('./svgo/plugins.js'); | ||
const JSAPI = require('./svgo/jsAPI.js'); | ||
const { encodeSVGDatauri } = require('./svgo/tools.js'); | ||
var SVGO = function(config) { | ||
this.config = CONFIG(config); | ||
}; | ||
exports.extendDefaultPlugins = extendDefaultPlugins; | ||
SVGO.prototype.optimize = function(svgstr, info) { | ||
info = info || {}; | ||
return new Promise((resolve, reject) => { | ||
if (this.config.error) { | ||
reject(this.config.error); | ||
return; | ||
} | ||
var config = this.config, | ||
maxPassCount = config.multipass ? 10 : 1, | ||
counter = 0, | ||
prevResultSize = Number.POSITIVE_INFINITY, | ||
optimizeOnceCallback = (svgjs) => { | ||
if (svgjs.error) { | ||
reject(svgjs.error); | ||
return; | ||
} | ||
info.multipassCount = counter; | ||
if (++counter < maxPassCount && svgjs.data.length < prevResultSize) { | ||
prevResultSize = svgjs.data.length; | ||
this._optimizeOnce(svgjs.data, info, optimizeOnceCallback); | ||
} else { | ||
if (config.datauri) { | ||
svgjs.data = encodeSVGDatauri(svgjs.data, config.datauri); | ||
} | ||
if (info && info.path) { | ||
svgjs.path = info.path; | ||
} | ||
resolve(svgjs); | ||
} | ||
}; | ||
this._optimizeOnce(svgstr, info, optimizeOnceCallback); | ||
}); | ||
const optimize = (svgstr, config) => { | ||
if (config == null) { | ||
config = {}; | ||
} | ||
if (typeof config !== 'object') { | ||
throw Error('Config should be an object') | ||
} | ||
const maxPassCount = config.multipass ? 10 : 1; | ||
let prevResultSize = Number.POSITIVE_INFINITY; | ||
let svgjs = null; | ||
const info = {} | ||
if (config.path != null) { | ||
info.path = config.path; | ||
} | ||
for (let i = 0; i < maxPassCount; i += 1) { | ||
svgjs = svg2js(svgstr); | ||
if (svgjs.error == null) { | ||
const plugins = config.plugins || defaultPlugins; | ||
if (Array.isArray(plugins) === false) { | ||
throw Error('Invalid plugins list. Provided \'plugins\' in config should be an array.'); | ||
} | ||
const resolvedPlugins = plugins.map(plugin => resolvePluginConfig(plugin, config)) | ||
svgjs = invokePlugins(svgjs, info, resolvedPlugins); | ||
} | ||
svgjs = js2svg(svgjs, config.js2svg); | ||
if (svgjs.error) { | ||
throw Error(svgjs.error); | ||
} | ||
info.multipassCount = i; | ||
if (svgjs.data.length < prevResultSize) { | ||
prevResultSize = svgjs.data.length | ||
} else { | ||
if (config.datauri) { | ||
svgjs.data = encodeSVGDatauri(svgjs.data, config.datauri); | ||
} | ||
if (config.path != null) { | ||
svgjs.path = config.path; | ||
} | ||
return svgjs; | ||
} | ||
} | ||
return svgjs; | ||
}; | ||
exports.optimize = optimize; | ||
SVGO.prototype._optimizeOnce = function(svgstr, info, callback) { | ||
var config = this.config; | ||
SVG2JS(svgstr, function(svgjs) { | ||
if (svgjs.error) { | ||
callback(svgjs); | ||
return; | ||
} | ||
svgjs = PLUGINS(svgjs, info, config.plugins); | ||
callback(JS2SVG(svgjs, config.js2svg)); | ||
}); | ||
}; | ||
/** | ||
* The factory that creates a content item with the helper methods. | ||
* | ||
* @param {Object} data which passed to jsAPI constructor | ||
* @param {Object} data which is passed to jsAPI constructor | ||
* @returns {JSAPI} content item | ||
*/ | ||
SVGO.prototype.createContentItem = function(data) { | ||
return new JSAPI(data); | ||
const createContentItem = (data) => { | ||
return new JSAPI(data); | ||
}; | ||
SVGO.Config = CONFIG; | ||
module.exports = SVGO; | ||
// Offer ES module interop compatibility. | ||
module.exports.default = SVGO; | ||
exports.createContentItem = createContentItem; |
/* jshint quotmark: false */ | ||
'use strict'; | ||
var FS = require('fs'), | ||
PATH = require('path'), | ||
chalk = require('chalk'), | ||
mkdirp = require('mkdirp'), | ||
promisify = require('util.promisify'), | ||
readdir = promisify(FS.readdir), | ||
readFile = promisify(FS.readFile), | ||
writeFile = promisify(FS.writeFile), | ||
SVGO = require('../svgo.js'), | ||
YAML = require('js-yaml'), | ||
PKG = require('../../package.json'), | ||
encodeSVGDatauri = require('./tools.js').encodeSVGDatauri, | ||
decodeSVGDatauri = require('./tools.js').decodeSVGDatauri, | ||
checkIsDir = require('./tools.js').checkIsDir, | ||
regSVGFile = /\.svg$/, | ||
noop = () => {}, | ||
svgo; | ||
const FS = require('fs'); | ||
const PATH = require('path'); | ||
const chalk = require('chalk'); | ||
const { loadConfig, optimize } = require('../svgo-node.js'); | ||
const pluginsMap = require('../../plugins/plugins.js'); | ||
const PKG = require('../../package.json'); | ||
const { encodeSVGDatauri, decodeSVGDatauri } = require('./tools.js'); | ||
const regSVGFile = /\.svg$/; | ||
const noop = () => {}; | ||
/** | ||
* Command-Option-Argument. | ||
* | ||
* @see https://github.com/veged/coa | ||
* Synchronously check if path is a directory. Tolerant to errors like ENOENT. | ||
* @param {string} path | ||
*/ | ||
module.exports = require('coa').Cmd() | ||
.helpful() | ||
.name(PKG.name) | ||
.title(PKG.description) | ||
.opt() | ||
.name('version').title('Version') | ||
.short('v').long('version') | ||
.only() | ||
.flag() | ||
.act(function() { | ||
// output the version to stdout instead of stderr if returned | ||
process.stdout.write(PKG.version + '\n'); | ||
// coa will run `.toString` on the returned value and send it to stderr | ||
return ''; | ||
function checkIsDir(path) { | ||
try { | ||
return FS.lstatSync(path).isDirectory(); | ||
} catch(e) { | ||
return false; | ||
} | ||
} | ||
module.exports = function makeProgram(program) { | ||
program | ||
.name(PKG.name) | ||
.description(PKG.description, { | ||
INPUT: 'Alias to --input' | ||
}) | ||
.end() | ||
.opt() | ||
.name('input').title('Input file, "-" for STDIN') | ||
.short('i').long('input') | ||
.arr() | ||
.val(function(val) { | ||
return val || this.reject("Option '--input' must have a value."); | ||
}) | ||
.end() | ||
.opt() | ||
.name('string').title('Input SVG data string') | ||
.short('s').long('string') | ||
.end() | ||
.opt() | ||
.name('folder').title('Input folder, optimize and rewrite all *.svg files') | ||
.short('f').long('folder') | ||
.val(function(val) { | ||
return val || this.reject("Option '--folder' must have a value."); | ||
}) | ||
.end() | ||
.opt() | ||
.name('output').title('Output file or folder (by default the same as the input), "-" for STDOUT') | ||
.short('o').long('output') | ||
.arr() | ||
.val(function(val) { | ||
return val || this.reject("Option '--output' must have a value."); | ||
}) | ||
.end() | ||
.opt() | ||
.name('precision').title('Set number of digits in the fractional part, overrides plugins params') | ||
.short('p').long('precision') | ||
.val(function(val) { | ||
return !isNaN(val) ? val : this.reject("Option '--precision' must be an integer number"); | ||
}) | ||
.end() | ||
.opt() | ||
.name('config').title('Config file or JSON string to extend or replace default') | ||
.long('config') | ||
.val(function(val) { | ||
return val || this.reject("Option '--config' must have a value."); | ||
}) | ||
.end() | ||
.opt() | ||
.name('disable').title('Disable plugin by name, "--disable={PLUGIN1,PLUGIN2}" for multiple plugins (*nix)') | ||
.long('disable') | ||
.arr() | ||
.val(function(val) { | ||
return val || this.reject("Option '--disable' must have a value."); | ||
}) | ||
.end() | ||
.opt() | ||
.name('enable').title('Enable plugin by name, "--enable={PLUGIN3,PLUGIN4}" for multiple plugins (*nix)') | ||
.long('enable') | ||
.arr() | ||
.val(function(val) { | ||
return val || this.reject("Option '--enable' must have a value."); | ||
}) | ||
.end() | ||
.opt() | ||
.name('datauri').title('Output as Data URI string (base64, URI encoded or unencoded)') | ||
.long('datauri') | ||
.val(function(val) { | ||
return val || this.reject("Option '--datauri' must have one of the following values: 'base64', 'enc' or 'unenc'"); | ||
}) | ||
.end() | ||
.opt() | ||
.name('multipass').title('Pass over SVGs multiple times to ensure all optimizations are applied') | ||
.long('multipass') | ||
.flag() | ||
.end() | ||
.opt() | ||
.name('pretty').title('Make SVG pretty printed') | ||
.long('pretty') | ||
.flag() | ||
.end() | ||
.opt() | ||
.name('indent').title('Indent number when pretty printing SVGs') | ||
.long('indent') | ||
.val(function(val) { | ||
return !isNaN(val) ? val : this.reject("Option '--indent' must be an integer number"); | ||
}) | ||
.end() | ||
.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') | ||
.short('q').long('quiet') | ||
.flag() | ||
.end() | ||
.opt() | ||
.name('show-plugins').title('Show available plugins and exit') | ||
.long('show-plugins') | ||
.flag() | ||
.end() | ||
.arg() | ||
.name('input').title('Alias to --input') | ||
.arr() | ||
.end() | ||
.act(function(opts, args) { | ||
var input = opts.input || args.input, | ||
output = opts.output, | ||
config = {}; | ||
.version(PKG.version, '-v, --version') | ||
.arguments('[INPUT...]') | ||
.option('-i, --input <INPUT...>', 'Input files, "-" for STDIN') | ||
.option('-s, --string <STRING>', 'Input SVG data string') | ||
.option('-f, --folder <FOLDER>', 'Input folder, optimize and rewrite all *.svg files') | ||
.option('-o, --output <OUTPUT...>', 'Output file or folder (by default the same as the input), "-" for STDOUT') | ||
.option('-p, --precision <INTEGER>', 'Set number of digits in the fractional part, overrides plugins params') | ||
.option('--config <CONFIG>', 'Config file or JSON string to extend or replace default') | ||
.option('--datauri <FORMAT>', 'Output as Data URI string (base64), URI encoded (enc) or unencoded (unenc)') | ||
.option('--multipass', 'Pass over SVGs multiple times to ensure all optimizations are applied') | ||
.option('--pretty', 'Make SVG pretty printed') | ||
.option('--indent <INTEGER>', 'Indent number when pretty printing SVGs') | ||
.option('-r, --recursive', 'Use with \'-f\'. Optimizes *.svg files in folders recursively.') | ||
.option('-q, --quiet', 'Only output error messages, not regular status messages') | ||
.option('--show-plugins', 'Show available plugins and exit') | ||
.action(action); | ||
} | ||
// --show-plugins | ||
if (opts['show-plugins']) { | ||
showAvailablePlugins(); | ||
return; | ||
} | ||
async function action(args, opts) { | ||
var input = opts.input || args; | ||
var output = opts.output; | ||
var config = {} | ||
// w/o anything | ||
if ( | ||
(!input || input[0] === '-') && | ||
!opts.string && | ||
!opts.stdin && | ||
!opts.folder && | ||
process.stdin.isTTY === true | ||
) return this.usage(); | ||
if (typeof process == 'object' && process.versions && process.versions.node && PKG && PKG.engines.node) { | ||
var nodeVersion = String(PKG.engines.node).match(/\d*(\.\d+)*/)[0]; | ||
if (parseFloat(process.versions.node) < parseFloat(nodeVersion)) { | ||
return printErrorAndExit(`Error: ${PKG.name} requires Node.js version ${nodeVersion} or higher.`); | ||
} | ||
if (opts.precision != null) { | ||
const number = Number.parseInt(opts.precision, 0); | ||
if (Number.isNaN(number)) { | ||
console.error("error: option '-p, --precision' argument must be an integer number"); | ||
process.exit(1) | ||
} else { | ||
opts.precision = number; | ||
} | ||
} | ||
// --config | ||
if (opts.config) { | ||
// string | ||
if (opts.config.charAt(0) === '{') { | ||
try { | ||
config = JSON.parse(opts.config); | ||
} catch (e) { | ||
return printErrorAndExit(`Error: Couldn't parse config JSON.\n${String(e)}`); | ||
} | ||
// external file | ||
} else { | ||
var configPath = PATH.resolve(opts.config), | ||
configData; | ||
try { | ||
// require() adds some weird output on YML files | ||
configData = FS.readFileSync(configPath, 'utf8'); | ||
config = JSON.parse(configData); | ||
} catch (err) { | ||
if (err.code === 'ENOENT') { | ||
return printErrorAndExit(`Error: couldn't find config file '${opts.config}'.`); | ||
} else if (err.code === 'EISDIR') { | ||
return printErrorAndExit(`Error: directory '${opts.config}' is not a config file.`); | ||
} | ||
config = YAML.safeLoad(configData); | ||
config.__DIR = PATH.dirname(configPath); // will use it to resolve custom plugins defined via path | ||
if (!config || Array.isArray(config)) { | ||
return printErrorAndExit(`Error: invalid config file '${opts.config}'.`); | ||
} | ||
} | ||
} | ||
if (opts.datauri != null) { | ||
if (opts.datauri !== 'base64' && opts.datauri !== 'enc' && opts.datauri !== 'unenc') { | ||
console.error("error: option '--datauri' must have one of the following values: 'base64', 'enc' or 'unenc'") | ||
process.exit(1) | ||
} | ||
} | ||
// --quiet | ||
if (opts.quiet) { | ||
config.quiet = opts.quiet; | ||
if (opts.indent != null) { | ||
const number = Number.parseInt(opts.indent, 0); | ||
if (Number.isNaN(number)) { | ||
console.error("error: option '--indent' argument must be an integer number"); | ||
process.exit(1); | ||
} else { | ||
opts.indent = number; | ||
} | ||
} | ||
// --recursive | ||
if (opts.recursive) { | ||
config.recursive = opts.recursive; | ||
} | ||
// --show-plugins | ||
if (opts.showPlugins) { | ||
showAvailablePlugins(); | ||
return; | ||
} | ||
// --precision | ||
if (opts.precision) { | ||
var precision = Math.min(Math.max(0, parseInt(opts.precision)), 20); | ||
if (!isNaN(precision)) { | ||
config.floatPrecision = precision; | ||
} | ||
} | ||
// w/o anything | ||
if ( | ||
(input.length === 0 || input[0] === '-') && | ||
!opts.string && | ||
!opts.stdin && | ||
!opts.folder && | ||
process.stdin.isTTY === true | ||
) return program.help(); | ||
// --disable | ||
if (opts.disable) { | ||
changePluginsState(opts.disable, false, config); | ||
if (typeof process == 'object' && process.versions && process.versions.node && PKG && PKG.engines.node) { | ||
var nodeVersion = String(PKG.engines.node).match(/\d*(\.\d+)*/)[0]; | ||
if (parseFloat(process.versions.node) < parseFloat(nodeVersion)) { | ||
return printErrorAndExit(`Error: ${PKG.name} requires Node.js version ${nodeVersion} or higher.`); | ||
} | ||
} | ||
// --enable | ||
if (opts.enable) { | ||
changePluginsState(opts.enable, true, config); | ||
} | ||
// --config | ||
try { | ||
const loadedConfig = await loadConfig(opts.config); | ||
if (loadedConfig != null) { | ||
config = loadedConfig; | ||
} | ||
} catch (error) { | ||
return printErrorAndExit(error.message); | ||
} | ||
// --multipass | ||
if (opts.multipass) { | ||
config.multipass = true; | ||
} | ||
// --quiet | ||
if (opts.quiet) { | ||
config.quiet = opts.quiet; | ||
} | ||
// --pretty | ||
if (opts.pretty) { | ||
config.js2svg = config.js2svg || {}; | ||
config.js2svg.pretty = true; | ||
var indent; | ||
if (opts.indent && !isNaN(indent = parseInt(opts.indent))) { | ||
config.js2svg.indent = indent; | ||
} | ||
} | ||
// --recursive | ||
if (opts.recursive) { | ||
config.recursive = opts.recursive; | ||
} | ||
svgo = new SVGO(config); | ||
// --precision | ||
if (opts.precision != null) { | ||
var precision = Math.min(Math.max(0, opts.precision), 20); | ||
config.floatPrecision = precision; | ||
} | ||
// --output | ||
if (output) { | ||
if (input && input[0] != '-') { | ||
if (output.length == 1 && checkIsDir(output[0])) { | ||
var dir = output[0]; | ||
for (var i = 0; i < input.length; i++) { | ||
output[i] = checkIsDir(input[i]) ? input[i] : PATH.resolve(dir, PATH.basename(input[i])); | ||
} | ||
} else if (output.length < input.length) { | ||
output = output.concat(input.slice(output.length)); | ||
} | ||
} | ||
} else if (input) { | ||
output = input; | ||
} else if (opts.string) { | ||
output = '-'; | ||
} | ||
// --multipass | ||
if (opts.multipass) { | ||
config.multipass = true; | ||
} | ||
if (opts.datauri) { | ||
config.datauri = opts.datauri; | ||
// --pretty | ||
if (opts.pretty) { | ||
config.js2svg = config.js2svg || {}; | ||
config.js2svg.pretty = true; | ||
if (opts.indent != null) { | ||
config.js2svg.indent = indent; | ||
} | ||
} | ||
// --folder | ||
if (opts.folder) { | ||
var ouputFolder = output && output[0] || opts.folder; | ||
return optimizeFolder(config, opts.folder, ouputFolder).then(noop, printErrorAndExit); | ||
} | ||
// --input | ||
if (input) { | ||
// STDIN | ||
if (input[0] === '-') { | ||
return new Promise((resolve, reject) => { | ||
var data = '', | ||
file = output[0]; | ||
process.stdin | ||
.on('data', chunk => data += chunk) | ||
.once('end', () => processSVGData(config, {input: 'string'}, data, file).then(resolve, reject)); | ||
}); | ||
// file | ||
} else { | ||
return Promise.all(input.map((file, n) => optimizeFile(config, file, output[n]))) | ||
.then(noop, printErrorAndExit); | ||
// --output | ||
if (output) { | ||
if (input.length && input[0] != '-') { | ||
if (output.length == 1 && checkIsDir(output[0])) { | ||
var dir = output[0]; | ||
for (var i = 0; i < input.length; i++) { | ||
output[i] = checkIsDir(input[i]) ? input[i] : PATH.resolve(dir, PATH.basename(input[i])); | ||
} | ||
} else if (output.length < input.length) { | ||
output = output.concat(input.slice(output.length)); | ||
} | ||
// --string | ||
} else if (opts.string) { | ||
var data = decodeSVGDatauri(opts.string); | ||
return processSVGData(config, {input: 'string'}, data, output[0]); | ||
} | ||
}); | ||
} else if (input.length) { | ||
output = input; | ||
} else if (opts.string) { | ||
output = '-'; | ||
} | ||
/** | ||
* Change plugins state by names array. | ||
* | ||
* @param {Array} names plugins names | ||
* @param {Boolean} state active state | ||
* @param {Object} config original config | ||
* @return {Object} changed config | ||
*/ | ||
function changePluginsState(names, state, config) { | ||
names.forEach(flattenPluginsCbk); | ||
if (opts.datauri) { | ||
config.datauri = opts.datauri; | ||
} | ||
// extend config | ||
if (config.plugins) { | ||
for (var name of names) { | ||
var matched = false, | ||
key; | ||
// --folder | ||
if (opts.folder) { | ||
var ouputFolder = output && output[0] || opts.folder; | ||
return optimizeFolder(config, opts.folder, ouputFolder).then(noop, printErrorAndExit); | ||
} | ||
for (var plugin of config.plugins) { | ||
// get plugin name | ||
if (typeof plugin === 'object') { | ||
key = Object.keys(plugin)[0]; | ||
} else { | ||
key = plugin; | ||
} | ||
// --input | ||
if (input.length !== 0) { | ||
// STDIN | ||
if (input[0] === '-') { | ||
return new Promise((resolve, reject) => { | ||
var data = '', | ||
file = output[0]; | ||
// if there is such a plugin name | ||
if (key === name) { | ||
// don't replace plugin's params with true | ||
if (typeof plugin[key] !== 'object' || !state) { | ||
plugin[key] = state; | ||
} | ||
// mark it as matched | ||
matched = true; | ||
} | ||
} | ||
// if not matched and current config is not full | ||
if (!matched && !config.full) { | ||
// push new plugin Object | ||
config.plugins.push({ [name]: state }); | ||
matched = true; | ||
} | ||
process.stdin | ||
.on('data', chunk => data += chunk) | ||
.once('end', () => processSVGData(config, {input: 'string'}, data, file).then(resolve, reject)); | ||
}); | ||
// file | ||
} else { | ||
return Promise.all(input.map((file, n) => optimizeFile(config, file, output[n]))) | ||
.then(noop, printErrorAndExit); | ||
} | ||
// just push | ||
} else { | ||
config.plugins = names.map(name => ({ [name]: state })); | ||
} | ||
return config; | ||
} | ||
/** | ||
* 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(','); | ||
// --string | ||
} else if (opts.string) { | ||
var data = decodeSVGDatauri(opts.string); | ||
if(split.length > 1) { | ||
names[index] = split.shift(); | ||
names.push.apply(names, split); | ||
return processSVGData(config, {input: 'string'}, data, output[0]); | ||
} | ||
} | ||
@@ -382,3 +209,3 @@ | ||
} | ||
return readdir(dir).then(files => processDirectory(config, dir, files, output)); | ||
return FS.promises.readdir(dir).then(files => processDirectory(config, dir, files, output)); | ||
} | ||
@@ -443,3 +270,3 @@ | ||
function optimizeFile(config, file, output) { | ||
return readFile(file, 'utf8').then( | ||
return FS.promises.readFile(file, 'utf8').then( | ||
data => processSVGData(config, {input: 'file', path: file}, data, output, file), | ||
@@ -462,20 +289,19 @@ error => checkOptimizeFileError(config, file, output, error) | ||
return svgo.optimize(data, info).then(function(result) { | ||
if (config.datauri) { | ||
result.data = encodeSVGDatauri(result.data, config.datauri); | ||
} | ||
var resultFileSize = Buffer.byteLength(result.data, 'utf8'), | ||
processingTime = Date.now() - startTime; | ||
const result = optimize(data, { ...config, ...info }); | ||
if (config.datauri) { | ||
result.data = encodeSVGDatauri(result.data, config.datauri); | ||
} | ||
var resultFileSize = Buffer.byteLength(result.data, 'utf8'), | ||
processingTime = Date.now() - startTime; | ||
return writeOutput(input, output, result.data).then(function() { | ||
if (!config.quiet && output != '-') { | ||
if (input) { | ||
console.log(`\n${PATH.basename(input)}:`); | ||
} | ||
printTimeInfo(processingTime); | ||
printProfitInfo(prevFileSize, resultFileSize); | ||
return writeOutput(input, output, result.data).then(function() { | ||
if (!config.quiet && output != '-') { | ||
if (input) { | ||
console.log(`\n${PATH.basename(input)}:`); | ||
} | ||
}, | ||
error => Promise.reject(new Error(error.code === 'ENOTDIR' ? `Error: output '${output}' is not a directory.` : error))); | ||
}); | ||
printTimeInfo(processingTime); | ||
printProfitInfo(prevFileSize, resultFileSize); | ||
} | ||
}, | ||
error => Promise.reject(new Error(error.code === 'ENOTDIR' ? `Error: output '${output}' is not a directory.` : error))); | ||
} | ||
@@ -496,5 +322,5 @@ | ||
mkdirp.sync(PATH.dirname(output)); | ||
FS.mkdirSync(PATH.dirname(output), { recursive: true }); | ||
return writeFile(output, data, 'utf8').catch(error => checkWriteFileError(input, output, data, error)); | ||
return FS.promises.writeFile(output, data, 'utf8').catch(error => checkWriteFileError(input, output, data, error)); | ||
} | ||
@@ -554,3 +380,3 @@ | ||
if (error.code == 'EISDIR' && input) { | ||
return writeFile(PATH.resolve(output, PATH.basename(input)), data, 'utf8'); | ||
return FS.promises.writeFile(PATH.resolve(output, PATH.basename(input)), data, 'utf8'); | ||
} else { | ||
@@ -565,10 +391,7 @@ return Promise.reject(error); | ||
function showAvailablePlugins() { | ||
console.log('Currently available plugins:'); | ||
// Flatten an array of plugins grouped per type, sort and write output | ||
var list = [].concat.apply([], new SVGO().config.plugins) | ||
.sort((a, b) => a.name.localeCompare(b.name)) | ||
.map(plugin => ` [ ${chalk.green(plugin.name)} ] ${plugin.description}`) | ||
.join('\n'); | ||
console.log(list); | ||
const list = Object.entries(pluginsMap) | ||
.sort(([a], [b]) => a.localeCompare(b)) | ||
.map(([name, plugin]) => ` [ ${chalk.green(name)} ] ${plugin.description}`) | ||
.join('\n'); | ||
console.log('Currently available plugins:\n' + list); | ||
} | ||
@@ -586,1 +409,3 @@ | ||
} | ||
module.exports.checkIsDir = checkIsDir; |
'use strict'; | ||
var FS = require('fs'); | ||
var PATH = require('path'); | ||
var yaml = require('js-yaml'); | ||
const pluginsMap = require('../../plugins/plugins.js'); | ||
/** | ||
* Read and/or extend/replace default config file, | ||
* prepare and optimize plugins array. | ||
* | ||
* @param {Object} [config] input config | ||
* @return {Object} output config | ||
*/ | ||
module.exports = function(config) { | ||
const pluginsOrder = [ | ||
'removeDoctype', | ||
'removeXMLProcInst', | ||
'removeComments', | ||
'removeMetadata', | ||
'removeXMLNS', | ||
'removeEditorsNSData', | ||
'cleanupAttrs', | ||
'inlineStyles', | ||
'minifyStyles', | ||
'convertStyleToAttrs', | ||
'cleanupIDs', | ||
'prefixIds', | ||
'removeRasterImages', | ||
'removeUselessDefs', | ||
'cleanupNumericValues', | ||
'cleanupListOfValues', | ||
'convertColors', | ||
'removeUnknownsAndDefaults', | ||
'removeNonInheritableGroupAttrs', | ||
'removeUselessStrokeAndFill', | ||
'removeViewBox', | ||
'cleanupEnableBackground', | ||
'removeHiddenElems', | ||
'removeEmptyText', | ||
'convertShapeToPath', | ||
'convertEllipseToCircle', | ||
'moveElemsAttrsToGroup', | ||
'moveGroupAttrsToElems', | ||
'collapseGroups', | ||
'convertPathData', | ||
'convertTransform', | ||
'removeEmptyAttrs', | ||
'removeEmptyContainers', | ||
'mergePaths', | ||
'removeUnusedNS', | ||
'sortAttrs', | ||
'sortDefsChildren', | ||
'removeTitle', | ||
'removeDesc', | ||
'removeDimensions', | ||
'removeAttrs', | ||
'removeAttributesBySelector', | ||
'removeElementsByAttr', | ||
'addClassesToSVGElement', | ||
'removeStyleElement', | ||
'removeScriptElement', | ||
'addAttributesToSVGElement', | ||
'removeOffCanvasPaths', | ||
'reusePaths', | ||
]; | ||
const defaultPlugins = pluginsOrder.filter(name => pluginsMap[name].active); | ||
exports.defaultPlugins = defaultPlugins; | ||
var defaults; | ||
config = typeof config == 'object' && config || {}; | ||
if (config.plugins && !Array.isArray(config.plugins)) { | ||
return { error: 'Error: Invalid plugins list. Provided \'plugins\' in config should be an array.' }; | ||
} | ||
if (config.full) { | ||
defaults = config; | ||
if (Array.isArray(defaults.plugins)) { | ||
defaults.plugins = preparePluginsArray(config, defaults.plugins); | ||
} | ||
const extendDefaultPlugins = (plugins) => { | ||
const extendedPlugins = pluginsOrder.map(name => ({ name, ...pluginsMap[name] })); | ||
for (const plugin of plugins) { | ||
const resolvedPlugin = resolvePluginConfig(plugin, {}); | ||
const index = pluginsOrder.indexOf(resolvedPlugin.name); | ||
if (index === -1) { | ||
extendedPlugins.push(plugin); | ||
} else { | ||
defaults = Object.assign({}, yaml.safeLoad(FS.readFileSync(__dirname + '/../../.svgo.yml', 'utf8'))); | ||
defaults.plugins = preparePluginsArray(config, defaults.plugins || []); | ||
defaults = extendConfig(defaults, config); | ||
extendedPlugins[index] = plugin; | ||
} | ||
if ('floatPrecision' in config && Array.isArray(defaults.plugins)) { | ||
defaults.plugins.forEach(function(plugin) { | ||
if (plugin.params && ('floatPrecision' in plugin.params)) { | ||
// Don't touch default plugin params | ||
plugin.params = Object.assign({}, plugin.params, { floatPrecision: config.floatPrecision }); | ||
} | ||
}); | ||
} | ||
if ('datauri' in config) { | ||
defaults.datauri = config.datauri; | ||
} | ||
if (Array.isArray(defaults.plugins)) { | ||
defaults.plugins = optimizePluginsArray(defaults.plugins); | ||
} | ||
return defaults; | ||
}; | ||
/** | ||
* Require() all plugins in array. | ||
* | ||
* @param {Object} config | ||
* @param {Array} plugins input plugins array | ||
* @return {Array} input plugins array of arrays | ||
*/ | ||
function preparePluginsArray(config, plugins) { | ||
var plugin, | ||
key; | ||
return plugins.map(function(item) { | ||
// {} | ||
if (typeof item === 'object') { | ||
key = Object.keys(item)[0]; | ||
// custom | ||
if (typeof item[key] === 'object' && item[key].fn && typeof item[key].fn === 'function') { | ||
plugin = setupCustomPlugin(key, item[key]); | ||
} else { | ||
plugin = setPluginActiveState( | ||
loadPlugin(config, key, item[key].path), | ||
item, | ||
key | ||
); | ||
plugin.name = key; | ||
} | ||
// name | ||
} else { | ||
plugin = loadPlugin(config, item); | ||
plugin.name = item; | ||
if (typeof plugin.params === 'object') { | ||
plugin.params = Object.assign({}, plugin.params); | ||
} | ||
} | ||
return plugin; | ||
}); | ||
} | ||
return extendedPlugins; | ||
} | ||
exports.extendDefaultPlugins = extendDefaultPlugins; | ||
/** | ||
* Extend plugins with the custom config object. | ||
* | ||
* @param {Array} plugins input plugins | ||
* @param {Object} config config | ||
* @return {Array} output plugins | ||
*/ | ||
function extendConfig(defaults, config) { | ||
var key; | ||
// plugins | ||
if (config.plugins) { | ||
config.plugins.forEach(function(item) { | ||
// {} | ||
if (typeof item === 'object') { | ||
key = Object.keys(item)[0]; | ||
if (item[key] == null) { | ||
console.error(`Error: '${key}' plugin is misconfigured! Have you padded its content in YML properly?\n`); | ||
} | ||
// custom | ||
if (typeof item[key] === 'object' && item[key].fn && typeof item[key].fn === 'function') { | ||
defaults.plugins.push(setupCustomPlugin(key, item[key])); | ||
// 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 { | ||
defaults.plugins.forEach(function(plugin) { | ||
if (plugin.name === key) { | ||
plugin = setPluginActiveState(plugin, item, key); | ||
} | ||
}); | ||
} | ||
} | ||
}); | ||
const resolvePluginConfig = (plugin, config) => { | ||
let configParams = {}; | ||
if ('floatPrecision' in config) { | ||
configParams.floatPrecision = config.floatPrecision; | ||
} | ||
if (typeof plugin === 'string') { | ||
// resolve builtin plugin specified as string | ||
const pluginConfig = pluginsMap[plugin]; | ||
if (pluginConfig == null) { | ||
throw Error(`Unknown builtin plugin "${plugin}" specified.`); | ||
} | ||
defaults.multipass = config.multipass; | ||
// svg2js | ||
if (config.svg2js) { | ||
defaults.svg2js = config.svg2js; | ||
return { | ||
...pluginConfig, | ||
name: plugin, | ||
active: true, | ||
params: { ...pluginConfig.params, ...configParams } | ||
}; | ||
} | ||
if (typeof plugin === 'object' && plugin != null) { | ||
if (plugin.name == null) { | ||
throw Error(`Plugin name should be specified`); | ||
} | ||
// js2svg | ||
if (config.js2svg) { | ||
defaults.js2svg = config.js2svg; | ||
} | ||
return defaults; | ||
} | ||
/** | ||
* Setup and enable a custom plugin | ||
* | ||
* @param {String} plugin name | ||
* @param {Object} custom plugin | ||
* @return {Array} enabled plugin | ||
*/ | ||
function setupCustomPlugin(name, plugin) { | ||
plugin.active = true; | ||
plugin.params = Object.assign({}, plugin.params || {}); | ||
plugin.name = name; | ||
return plugin; | ||
} | ||
/** | ||
* Try to group sequential elements of plugins array. | ||
* | ||
* @param {Object} plugins input plugins | ||
* @return {Array} output plugins | ||
*/ | ||
function optimizePluginsArray(plugins) { | ||
var prev; | ||
return plugins.reduce(function(plugins, item) { | ||
if (prev && item.type == prev[0].type) { | ||
prev.push(item); | ||
} else { | ||
plugins.push(prev = [item]); | ||
} | ||
return plugins; | ||
}, []); | ||
} | ||
/** | ||
* 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); | ||
if (plugin.fn) { | ||
// resolve custom plugin with implementation | ||
return { | ||
active: true, | ||
...plugin, | ||
params: { configParams, ...plugin.params } | ||
}; | ||
} else { | ||
plugin = require(PATH.resolve(config.__DIR, path)); | ||
// resolve builtin plugin specified as object without implementation | ||
const pluginConfig = pluginsMap[plugin.name]; | ||
if (pluginConfig == null) { | ||
throw Error(`Unknown builtin plugin "${plugin.name}" specified.`); | ||
} | ||
return { | ||
...pluginConfig, | ||
active: true, | ||
...plugin, | ||
params: { ...pluginConfig.params, ...configParams, ...plugin.params } | ||
}; | ||
} | ||
return Object.assign({}, plugin); | ||
} | ||
} | ||
return null; | ||
}; | ||
exports.resolvePluginConfig = resolvePluginConfig; |
'use strict'; | ||
var values = require('object.values'); | ||
if (!Object.values) { | ||
values.shim(); | ||
} | ||
var CSSClassList = function(node) { | ||
@@ -138,2 +132,2 @@ this.parentNode = node; | ||
module.exports = CSSClassList; | ||
module.exports = CSSClassList; |
'use strict'; | ||
var cssSelect = require('css-select'); | ||
const { selectAll, selectOne, is } = require('css-select'); | ||
const svgoCssSelectAdapter = require('./css-select-adapter'); | ||
var svgoCssSelectAdapter = require('./css-select-adapter'); | ||
var cssSelectOpts = { | ||
@@ -344,3 +344,3 @@ xmlMode: true, | ||
var matchedEls = cssSelect(selectors, this, cssSelectOpts); | ||
var matchedEls = selectAll(selectors, this, cssSelectOpts); | ||
@@ -359,3 +359,3 @@ return matchedEls.length > 0 ? matchedEls : null; | ||
return cssSelect.selectOne(selectors, this, cssSelectOpts); | ||
return selectOne(selectors, this, cssSelectOpts); | ||
@@ -372,4 +372,4 @@ }; | ||
return cssSelect.is(this, selector, cssSelectOpts); | ||
return is(this, selector, cssSelectOpts); | ||
}; |
@@ -14,21 +14,29 @@ 'use strict'; | ||
module.exports = function(data, info, plugins) { | ||
plugins.forEach(function(group) { | ||
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; | ||
} | ||
}); | ||
return data; | ||
const perItemPlugins = []; | ||
const perItemReversePlugins = []; | ||
const fullPlugins = []; | ||
// Try to group sequential elements of plugins array | ||
for (const plugin of plugins) { | ||
switch(plugin.type) { | ||
case 'perItem': | ||
perItemPlugins.push(plugin); | ||
break; | ||
case 'perItemReverse': | ||
perItemReversePlugins.push(plugin); | ||
break; | ||
case 'full': | ||
fullPlugins.push(plugin); | ||
break; | ||
} | ||
} | ||
if (perItemPlugins.length !== 0) { | ||
data = perItem(data, info, perItemPlugins); | ||
} | ||
if (perItemReversePlugins.length !== 0) { | ||
data = perItem(data, info, perItemReversePlugins, true); | ||
} | ||
if (fullPlugins.length !== 0) { | ||
data = full(data, info, fullPlugins); | ||
} | ||
return data; | ||
}; | ||
@@ -35,0 +43,0 @@ |
@@ -22,5 +22,4 @@ 'use strict'; | ||
* @param {String} data input data | ||
* @param {Function} callback | ||
*/ | ||
module.exports = function(data, callback) { | ||
module.exports = function(data) { | ||
@@ -31,4 +30,3 @@ var sax = SAX.parser(config.strict, config), | ||
stack = [root], | ||
textContext = null, | ||
parsingError = false; | ||
textContext = null; | ||
@@ -168,19 +166,8 @@ function pushToContent(content) { | ||
sax.onend = function() { | ||
if (!this.error) { | ||
callback(root); | ||
} else { | ||
callback({ error: this.error.message }); | ||
} | ||
}; | ||
try { | ||
sax.write(data); | ||
sax.write(data).close(); | ||
return root; | ||
} catch (e) { | ||
callback({ error: e.message }); | ||
parsingError = true; | ||
return { error: e.message }; | ||
} | ||
if (!parsingError) sax.close(); | ||
@@ -187,0 +174,0 @@ function trim(elem) { |
'use strict'; | ||
var FS = require('fs'); | ||
/** | ||
@@ -143,14 +141,1 @@ * 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.3.2", | ||
"version": "2.0.0", | ||
"description": "Nodejs-based tool for optimizing SVG vector graphics files", | ||
@@ -30,2 +30,7 @@ "keywords": [ | ||
"url": "http://github.com/GreLI" | ||
}, | ||
{ | ||
"name": "Bogdan Chadkin", | ||
"email": "trysound@yandex.ru", | ||
"url": "http://github.com/TrySound" | ||
} | ||
@@ -37,6 +42,13 @@ ], | ||
}, | ||
"main": "./lib/svgo.js", | ||
"main": "./lib/svgo-node.js", | ||
"bin": { | ||
"svgo": "./bin/svgo" | ||
}, | ||
"files": [ | ||
"bin", | ||
"lib", | ||
"plugins", | ||
"dist", | ||
"examples" | ||
], | ||
"directories": { | ||
@@ -48,35 +60,32 @@ "bin": "./bin", | ||
"scripts": { | ||
"test": "set NODE_ENV=test && mocha", | ||
"lint": "jshint --show-non-errors .", | ||
"jshint": "npm run lint" | ||
"test": "nyc --reporter=html --reporter=text mocha \"test/*/_index.js\"", | ||
"test-browser": "rollup -c && node ./test/browser.js", | ||
"prepublishOnly": "rollup -c" | ||
}, | ||
"dependencies": { | ||
"chalk": "^2.4.1", | ||
"coa": "^2.0.2", | ||
"css-select": "^2.0.0", | ||
"chalk": "^4.1.0", | ||
"commander": "^7.1.0", | ||
"css-select": "^3.1.2", | ||
"css-select-base-adapter": "^0.1.1", | ||
"css-tree": "1.0.0-alpha.37", | ||
"csso": "^4.0.2", | ||
"js-yaml": "^3.13.1", | ||
"mkdirp": "~0.5.1", | ||
"object.values": "^1.1.0", | ||
"css-tree": "^1.1.2", | ||
"csso": "^4.2.0", | ||
"sax": "~1.2.4", | ||
"stable": "^0.1.8", | ||
"unquote": "~1.1.1", | ||
"util.promisify": "~1.0.0" | ||
"stable": "^0.1.8" | ||
}, | ||
"devDependencies": { | ||
"coveralls": "^3.0.7", | ||
"fs-extra": "~8.1.0", | ||
"istanbul": "~0.4.5", | ||
"jshint": "~2.10.2", | ||
"mocha": "~6.2.2", | ||
"mocha-istanbul": "~0.3.0", | ||
"mock-stdin": "~0.3.1", | ||
"should": "~13.2.3" | ||
"@rollup/plugin-commonjs": "^17.1.0", | ||
"@rollup/plugin-json": "^4.1.0", | ||
"@rollup/plugin-node-resolve": "^11.2.0", | ||
"chai": "^4.3.0", | ||
"del": "^6.0.0", | ||
"mocha": "^8.3.0", | ||
"mock-stdin": "^1.0.0", | ||
"nyc": "^15.1.0", | ||
"playwright": "^1.8.1", | ||
"rollup": "^2.39.0" | ||
}, | ||
"engines": { | ||
"node": ">=4.0.0" | ||
"node": ">=10.13.0" | ||
}, | ||
"license": "MIT" | ||
} |
@@ -61,11 +61,14 @@ 'use strict'; | ||
if (!params.force) { | ||
if (item.isElem(styleOrScript)) { | ||
var isNotEmpty = Boolean(item.content); | ||
if (item.isElem(styleOrScript) && isNotEmpty) { | ||
hasStyleOrScript = true; | ||
continue; | ||
} | ||
// Don't remove IDs if the whole SVG consists only of defs. | ||
if (item.isElem('defs') && item.parentNode.isElem('svg')) { | ||
if (item.isElem('svg')) { | ||
var hasDefsOnly = true; | ||
for (var j = i + 1; j < items.content.length; j++) { | ||
if (items.content[j].isElem()) { | ||
for (var j = 0; j < item.content.length; j++) { | ||
if (!item.content[j].isElem('defs')) { | ||
hasDefsOnly = false; | ||
@@ -72,0 +75,0 @@ break; |
@@ -517,3 +517,3 @@ 'use strict'; | ||
(instruction != 'h' && instruction != 'v') || | ||
(prev.data[0] >= 0) == (item.data[0] >= 0) | ||
(prev.data[0] >= 0) == (data[0] >= 0) | ||
)) { | ||
@@ -520,0 +520,0 @@ prev.data[0] += data[0]; |
@@ -112,7 +112,4 @@ 'use strict'; | ||
} catch (selectError) { | ||
if (selectError.constructor === SyntaxError) { | ||
// console.warn('Warning: Syntax error when trying to select \n\n' + selectorStr + '\n\n, skipped. Error details: ' + selectError); | ||
continue; | ||
} | ||
throw selectError; | ||
// console.warn('Warning: Syntax error when trying to select \n\n' + selectorStr + '\n\n, skipped. Error details: ' + selectError); | ||
continue; | ||
} | ||
@@ -119,0 +116,0 @@ |
@@ -15,6 +15,3 @@ 'use strict'; | ||
var path = require('path'), | ||
csstree = require('css-tree'), | ||
unquote = require('unquote'), | ||
var csstree = require('css-tree'), | ||
collections = require('./_collections.js'), | ||
@@ -25,2 +22,11 @@ referencesProps = collections.referencesProps, | ||
const unquote = (string) => { | ||
const first = string.charAt(0) | ||
if (first === "'" || first === '"') { | ||
if (first === string.charAt(string.length - 1)) { | ||
return string.slice(1, -1); | ||
} | ||
} | ||
return string; | ||
} | ||
@@ -118,3 +124,41 @@ // Escapes a string for being used as ID | ||
// prefixes begin/end attribute value | ||
var addPrefixToBeginEndAttr = function(attr) { | ||
if (!attrNotEmpty(attr)) { | ||
return; | ||
} | ||
var parts = attr.value.split('; ').map(function(val) { | ||
val = val.trim(); | ||
if (val.endsWith('.end') || val.endsWith('.start')) { | ||
var idPostfix = val.split('.'), | ||
id = idPostfix[0], | ||
postfix = idPostfix[1]; | ||
var idPrefixed = prefixId(`#${id}`); | ||
if (!idPrefixed) { | ||
return val; | ||
} | ||
idPrefixed = idPrefixed.slice(1); | ||
return `${idPrefixed}.${postfix}`; | ||
} else { | ||
return val; | ||
} | ||
}); | ||
attr.value = parts.join('; '); | ||
}; | ||
const getBasename = (path) => { | ||
// extract everything after latest slash or backslash | ||
const matched = path.match(/[\/\\]([^\/\\]+)$/); | ||
if (matched) { | ||
return matched[1]; | ||
} | ||
return ''; | ||
}; | ||
/** | ||
@@ -147,3 +191,3 @@ * Prefixes identifiers | ||
} else if (extra && extra.path && extra.path.length > 0) { | ||
var filename = path.basename(extra.path); | ||
var filename = getBasename(extra.path); | ||
prefix = filename; | ||
@@ -245,4 +289,6 @@ } | ||
addPrefixToBeginEndAttr(node.attrs.begin); | ||
addPrefixToBeginEndAttr(node.attrs.end); | ||
return node; | ||
}; |
@@ -9,4 +9,5 @@ 'use strict'; | ||
var SVGO = require('../lib/svgo.js'), | ||
_path = require('./_path.js'), | ||
const JSAPI = require('../lib/svgo/jsAPI.js'); | ||
var _path = require('./_path.js'), | ||
intersects = _path.intersects, | ||
@@ -101,3 +102,3 @@ path2js = _path.path2js, | ||
var path = new SVGO().createContentItem({ | ||
var path = new JSAPI({ | ||
elem: 'path', | ||
@@ -104,0 +105,0 @@ prefix: '', |
@@ -98,5 +98,3 @@ **english** | [русский](https://github.com/svg/svgo/blob/master/README.ru.md) | ||
-p PRECISION, --precision=PRECISION : Set number of digits in the fractional part, overrides plugins params | ||
--config=CONFIG : Config file or JSON string to extend or replace default | ||
--disable=PLUGIN : Disable plugin by name, "--disable=PLUGIN1,PLUGIN2" for multiple plugins | ||
--enable=PLUGIN : Enable plugin by name, "--enable=PLUGIN3,PLUGIN4" for multiple plugins | ||
--config=CONFIG : Config file to customize default behavior | ||
--datauri=DATAURI : Output as Data URI string (base64, URI encoded or unencoded) | ||
@@ -125,2 +123,3 @@ --multipass : Pass over SVGs multiple times to ensure all optimizations are applied | ||
``` | ||
Windows does not support glob expansion. The command above will not work on Windows. | ||
@@ -202,2 +201,3 @@ ```sh | ||
* as a Rollup plugin - [rollup-plugin-svgo](https://github.com/porsager/rollup-plugin-svgo) | ||
* as a Figma plugin - [Advanced SVG Export](https://www.figma.com/c/plugin/782713260363070260/Advanced-SVG-Export) | ||
@@ -204,0 +204,0 @@ ## Backers |
@@ -200,2 +200,3 @@ [english](https://github.com/svg/svgo/blob/master/README.md) | **русский** | ||
* как плагин для Rollup - [rollup-plugin-svgo](https://github.com/porsager/rollup-plugin-svgo) | ||
* как плагин для Figma - [Advanced SVG Export](https://www.figma.com/c/plugin/782713260363070260/Advanced-SVG-Export) | ||
@@ -202,0 +203,0 @@ ## Лицензия и копирайты |
Sorry, the diff of this file is not supported yet
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
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
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
Deprecated
MaintenanceThe maintainer of the package marked it as deprecated. This could indicate that a single version should not be used, or that the package is no longer maintained and any new vulnerabilities will not be fixed.
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
Unidentified License
License(Experimental) Something that seems like a license was found, but its contents could not be matched with a known license.
Found 1 instance in 1 package
1859516
8
75
0
100
54347
0
4
10
2
+ Addedcommander@^7.1.0
+ Addedansi-styles@4.3.0(transitive)
+ Addedchalk@4.1.2(transitive)
+ Addedcolor-convert@2.0.1(transitive)
+ Addedcolor-name@1.1.4(transitive)
+ Addedcommander@7.2.0(transitive)
+ Addedcss-select@3.1.2(transitive)
+ Addedcss-what@4.0.0(transitive)
+ Addeddom-serializer@1.4.1(transitive)
+ Addeddomhandler@4.3.1(transitive)
+ Addeddomutils@2.8.0(transitive)
+ Addedhas-flag@4.0.0(transitive)
+ Addednth-check@2.1.1(transitive)
+ Addedsupports-color@7.2.0(transitive)
- Removedcoa@^2.0.2
- Removedjs-yaml@^3.13.1
- Removedmkdirp@~0.5.1
- Removedobject.values@^1.1.0
- Removedunquote@~1.1.1
- Removedutil.promisify@~1.0.0
- Removed@types/q@1.5.8(transitive)
- Removedansi-styles@3.2.1(transitive)
- Removedargparse@1.0.10(transitive)
- Removedarray-buffer-byte-length@1.0.1(transitive)
- Removedarray.prototype.reduce@1.0.7(transitive)
- Removedarraybuffer.prototype.slice@1.0.3(transitive)
- Removedavailable-typed-arrays@1.0.7(transitive)
- Removedcall-bind@1.0.7(transitive)
- Removedchalk@2.4.2(transitive)
- Removedcoa@2.0.2(transitive)
- Removedcolor-convert@1.9.3(transitive)
- Removedcolor-name@1.1.3(transitive)
- Removedcss-select@2.1.0(transitive)
- Removedcss-tree@1.0.0-alpha.37(transitive)
- Removedcss-what@3.4.2(transitive)
- Removeddata-view-buffer@1.0.1(transitive)
- Removeddata-view-byte-length@1.0.1(transitive)
- Removeddata-view-byte-offset@1.0.0(transitive)
- Removeddefine-data-property@1.1.4(transitive)
- Removeddefine-properties@1.2.1(transitive)
- Removeddom-serializer@0.2.2(transitive)
- Removeddomelementtype@1.3.1(transitive)
- Removeddomutils@1.7.0(transitive)
- Removedes-abstract@1.23.3(transitive)
- Removedes-array-method-boxes-properly@1.0.0(transitive)
- Removedes-define-property@1.0.0(transitive)
- Removedes-errors@1.3.0(transitive)
- Removedes-object-atoms@1.0.0(transitive)
- Removedes-set-tostringtag@2.0.3(transitive)
- Removedes-to-primitive@1.2.1(transitive)
- Removedescape-string-regexp@1.0.5(transitive)
- Removedesprima@4.0.1(transitive)
- Removedfor-each@0.3.3(transitive)
- Removedfunction-bind@1.1.2(transitive)
- Removedfunction.prototype.name@1.1.6(transitive)
- Removedfunctions-have-names@1.2.3(transitive)
- Removedget-intrinsic@1.2.4(transitive)
- Removedget-symbol-description@1.0.2(transitive)
- Removedglobalthis@1.0.4(transitive)
- Removedgopd@1.0.1(transitive)
- Removedhas-bigints@1.0.2(transitive)
- Removedhas-flag@3.0.0(transitive)
- Removedhas-property-descriptors@1.0.2(transitive)
- Removedhas-proto@1.0.3(transitive)
- Removedhas-symbols@1.0.3(transitive)
- Removedhas-tostringtag@1.0.2(transitive)
- Removedhasown@2.0.2(transitive)
- Removedinternal-slot@1.0.7(transitive)
- Removedis-array-buffer@3.0.4(transitive)
- Removedis-bigint@1.0.4(transitive)
- Removedis-boolean-object@1.1.2(transitive)
- Removedis-callable@1.2.7(transitive)
- Removedis-data-view@1.0.1(transitive)
- Removedis-date-object@1.0.5(transitive)
- Removedis-negative-zero@2.0.3(transitive)
- Removedis-number-object@1.0.7(transitive)
- Removedis-regex@1.1.4(transitive)
- Removedis-shared-array-buffer@1.0.3(transitive)
- Removedis-string@1.0.7(transitive)
- Removedis-symbol@1.0.4(transitive)
- Removedis-typed-array@1.1.13(transitive)
- Removedis-weakref@1.0.2(transitive)
- Removedisarray@2.0.5(transitive)
- Removedjs-yaml@3.14.1(transitive)
- Removedmdn-data@2.0.4(transitive)
- Removedminimist@1.2.8(transitive)
- Removedmkdirp@0.5.6(transitive)
- Removednth-check@1.0.2(transitive)
- Removedobject-inspect@1.13.2(transitive)
- Removedobject-keys@1.1.1(transitive)
- Removedobject.assign@4.1.5(transitive)
- Removedobject.getownpropertydescriptors@2.1.8(transitive)
- Removedobject.values@1.2.0(transitive)
- Removedpossible-typed-array-names@1.0.0(transitive)
- Removedq@1.5.1(transitive)
- Removedregexp.prototype.flags@1.5.3(transitive)
- Removedsafe-array-concat@1.1.2(transitive)
- Removedsafe-regex-test@1.0.3(transitive)
- Removedset-function-length@1.2.2(transitive)
- Removedset-function-name@2.0.2(transitive)
- Removedside-channel@1.0.6(transitive)
- Removedsprintf-js@1.0.3(transitive)
- Removedstring.prototype.trim@1.2.9(transitive)
- Removedstring.prototype.trimend@1.0.8(transitive)
- Removedstring.prototype.trimstart@1.0.8(transitive)
- Removedsupports-color@5.5.0(transitive)
- Removedtyped-array-buffer@1.0.2(transitive)
- Removedtyped-array-byte-length@1.0.1(transitive)
- Removedtyped-array-byte-offset@1.0.2(transitive)
- Removedtyped-array-length@1.0.6(transitive)
- Removedunbox-primitive@1.0.2(transitive)
- Removedunquote@1.1.1(transitive)
- Removedutil.promisify@1.0.1(transitive)
- Removedwhich-boxed-primitive@1.0.2(transitive)
- Removedwhich-typed-array@1.1.15(transitive)
Updatedchalk@^4.1.0
Updatedcss-select@^3.1.2
Updatedcss-tree@^1.1.2
Updatedcsso@^4.2.0