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 1.3.2 to 2.0.0

dist/svgo.browser.js

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

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