Socket
Socket
Sign inDemoInstall

svgo

Package Overview
Dependencies
Maintainers
3
Versions
104
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

svgo - npm Package Compare versions

Comparing version 2.1.0 to 2.2.0

lib/path.js

188

lib/css-tools.js
'use strict';
var csstree = require('css-tree'),
List = csstree.List,
stable = require('stable'),
specificity = require('csso/lib/restructure/prepare/specificity');
var csstree = require('css-tree'),
List = csstree.List,
stable = require('stable'),
specificity = require('csso/lib/restructure/prepare/specificity');
/**

@@ -16,35 +15,44 @@ * Flatten a CSS AST to a selectors list.

function flattenToSelectors(cssAst) {
var selectors = [];
var selectors = [];
csstree.walk(cssAst, {visit: 'Rule', enter: function(node) {
if (node.type !== 'Rule') {
return;
}
csstree.walk(cssAst, {
visit: 'Rule',
enter: function (node) {
if (node.type !== 'Rule') {
return;
}
var atrule = this.atrule;
var rule = node;
var atrule = this.atrule;
var rule = node;
node.prelude.children.each(function(selectorNode, selectorItem) {
var selector = {
item: selectorItem,
atrule: atrule,
rule: rule,
pseudos: []
};
node.prelude.children.each(function (selectorNode, selectorItem) {
var selector = {
item: selectorItem,
atrule: atrule,
rule: rule,
pseudos: [],
};
selectorNode.children.each(function(selectorChildNode, selectorChildItem, selectorChildList) {
if (selectorChildNode.type === 'PseudoClassSelector' ||
selectorChildNode.type === 'PseudoElementSelector') {
selector.pseudos.push({
item: selectorChildItem,
list: selectorChildList
});
}
selectorNode.children.each(function (
selectorChildNode,
selectorChildItem,
selectorChildList
) {
if (
selectorChildNode.type === 'PseudoClassSelector' ||
selectorChildNode.type === 'PseudoElementSelector'
) {
selector.pseudos.push({
item: selectorChildItem,
list: selectorChildList,
});
selectors.push(selector);
}
});
}});
return selectors;
selectors.push(selector);
});
},
});
return selectors;
}

@@ -60,17 +68,19 @@

function filterByMqs(selectors, useMqs) {
return selectors.filter(function(selector) {
if (selector.atrule === null) {
return ~useMqs.indexOf('');
}
return selectors.filter(function (selector) {
if (selector.atrule === null) {
return ~useMqs.indexOf('');
}
var mqName = selector.atrule.name;
var mqStr = mqName;
if (selector.atrule.expression &&
selector.atrule.expression.children.first().type === 'MediaQueryList') {
var mqExpr = csstree.generate(selector.atrule.expression);
mqStr = [mqName, mqExpr].join(' ');
}
var mqName = selector.atrule.name;
var mqStr = mqName;
if (
selector.atrule.expression &&
selector.atrule.expression.children.first().type === 'MediaQueryList'
) {
var mqExpr = csstree.generate(selector.atrule.expression);
mqStr = [mqName, mqExpr].join(' ');
}
return ~useMqs.indexOf(mqStr);
});
return ~useMqs.indexOf(mqStr);
});
}

@@ -86,11 +96,13 @@

function filterByPseudos(selectors, usePseudos) {
return selectors.filter(function(selector) {
var pseudoSelectorsStr = csstree.generate({
type: 'Selector',
children: new List().fromArray(selector.pseudos.map(function(pseudo) {
return pseudo.item.data;
}))
});
return ~usePseudos.indexOf(pseudoSelectorsStr);
return selectors.filter(function (selector) {
var pseudoSelectorsStr = csstree.generate({
type: 'Selector',
children: new List().fromArray(
selector.pseudos.map(function (pseudo) {
return pseudo.item.data;
})
),
});
return ~usePseudos.indexOf(pseudoSelectorsStr);
});
}

@@ -105,10 +117,9 @@

function cleanPseudos(selectors) {
selectors.forEach(function(selector) {
selector.pseudos.forEach(function(pseudo) {
pseudo.list.remove(pseudo.item);
});
selectors.forEach(function (selector) {
selector.pseudos.forEach(function (pseudo) {
pseudo.list.remove(pseudo.item);
});
});
}
/**

@@ -123,14 +134,13 @@ * Compares two selector specificities.

function compareSpecificity(aSpecificity, bSpecificity) {
for (var i = 0; i < 4; i += 1) {
if (aSpecificity[i] < bSpecificity[i]) {
return -1;
} else if (aSpecificity[i] > bSpecificity[i]) {
return 1;
}
for (var i = 0; i < 4; i += 1) {
if (aSpecificity[i] < bSpecificity[i]) {
return -1;
} else if (aSpecificity[i] > bSpecificity[i]) {
return 1;
}
}
return 0;
return 0;
}
/**

@@ -144,12 +154,11 @@ * Compare two simple selectors.

function compareSimpleSelectorNode(aSimpleSelectorNode, bSimpleSelectorNode) {
var aSpecificity = specificity(aSimpleSelectorNode),
bSpecificity = specificity(bSimpleSelectorNode);
return compareSpecificity(aSpecificity, bSpecificity);
var aSpecificity = specificity(aSimpleSelectorNode),
bSpecificity = specificity(bSimpleSelectorNode);
return compareSpecificity(aSpecificity, bSpecificity);
}
function _bySelectorSpecificity(selectorA, selectorB) {
return compareSimpleSelectorNode(selectorA.item.data, selectorB.item.data);
return compareSimpleSelectorNode(selectorA.item.data, selectorB.item.data);
}
/**

@@ -162,6 +171,5 @@ * Sort selectors stably by their specificity.

function sortSelectors(selectors) {
return stable(selectors, _bySelectorSpecificity);
return stable(selectors, _bySelectorSpecificity);
}
/**

@@ -174,13 +182,12 @@ * Convert a css-tree AST style declaration to CSSStyleDeclaration property.

function csstreeToStyleDeclaration(declaration) {
var propertyName = declaration.property,
propertyValue = csstree.generate(declaration.value),
propertyPriority = (declaration.important ? 'important' : '');
return {
name: propertyName,
value: propertyValue,
priority: propertyPriority
};
var propertyName = declaration.property,
propertyValue = csstree.generate(declaration.value),
propertyPriority = declaration.important ? 'important' : '';
return {
name: propertyName,
value: propertyValue,
priority: propertyPriority,
};
}
/**

@@ -193,3 +200,3 @@ * Gets the CSS string of a style element

function getCssStr(elem) {
return elem.content[0].text || elem.content[0].cdata || [];
return elem.content[0].text || elem.content[0].cdata || [];
}

@@ -205,14 +212,13 @@

function setCssStr(elem, css) {
// in case of cdata field
if(elem.content[0].cdata) {
elem.content[0].cdata = css;
return elem.content[0].cdata;
}
// in case of cdata field
if (elem.content[0].cdata) {
elem.content[0].cdata = css;
return elem.content[0].cdata;
}
// in case of text field + if nothing was set yet
elem.content[0].text = css;
return elem.content[0].text;
// in case of text field + if nothing was set yet
elem.content[0].text = css;
return elem.content[0].text;
}
module.exports.flattenToSelectors = flattenToSelectors;

@@ -219,0 +225,0 @@

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

const importConfig = async configFile => {
const importConfig = async (configFile) => {
const config = require(configFile);

@@ -27,3 +27,3 @@ if (config == null || typeof config !== 'object' || Array.isArray(config)) {

}
}
};

@@ -41,3 +41,3 @@ const loadConfig = async (configFile, cwd = process.cwd()) => {

while (true) {
const file = path.join(dir, "svgo.config.js");
const file = path.join(dir, 'svgo.config.js');
if (await isFile(file)) {

@@ -44,0 +44,0 @@ return await importConfig(file);

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

resolvePluginConfig,
extendDefaultPlugins
extendDefaultPlugins,
} = require('./svgo/config.js');

@@ -32,3 +32,3 @@ const svg2js = require('./svgo/svg2js.js');

if (typeof config !== 'object') {
throw Error('Config should be an object')
throw Error('Config should be an object');
}

@@ -38,3 +38,3 @@ const maxPassCount = config.multipass ? 10 : 1;

let svgjs = null;
const info = {}
const info = {};
if (config.path != null) {

@@ -54,5 +54,9 @@ info.path = config.path;

if (Array.isArray(plugins) === false) {
throw Error('Invalid plugins list. Provided \'plugins\' in config should be an array.');
throw Error(
"Invalid plugins list. Provided 'plugins' in config should be an array."
);
}
const resolvedPlugins = plugins.map(plugin => resolvePluginConfig(plugin, config))
const resolvedPlugins = plugins.map((plugin) =>
resolvePluginConfig(plugin, config)
);
svgjs = invokePlugins(svgjs, info, resolvedPlugins);

@@ -65,3 +69,3 @@ svgjs = js2svg(svgjs, config.js2svg);

input = svgjs.data;
prevResultSize = svgjs.data.length
prevResultSize = svgjs.data.length;
} else {

@@ -68,0 +72,0 @@ if (config.datauri) {

@@ -17,178 +17,224 @@ 'use strict';

function checkIsDir(path) {
try {
return FS.lstatSync(path).isDirectory();
} catch(e) {
return false;
}
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'
})
.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>', 'Custom config file, only .js is supported')
.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);
}
program
.name(PKG.name)
.description(PKG.description, {
INPUT: 'Alias to --input',
})
.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>', 'Custom config file, only .js is supported')
.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);
};
async function action(args, opts, command) {
var input = opts.input || args;
var output = opts.output;
var config = {}
var input = opts.input || args;
var output = opts.output;
var config = {};
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;
}
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;
}
}
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)
}
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);
}
}
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;
}
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;
}
}
// --show-plugins
if (opts.showPlugins) {
showAvailablePlugins();
return;
}
// --show-plugins
if (opts.showPlugins) {
showAvailablePlugins();
return;
}
// w/o anything
if (
(input.length === 0 || input[0] === '-') &&
!opts.string &&
!opts.stdin &&
!opts.folder &&
process.stdin.isTTY === true
) {
return command.help();
}
// w/o anything
if (
(input.length === 0 || input[0] === '-') &&
!opts.string &&
!opts.stdin &&
!opts.folder &&
process.stdin.isTTY === true
) {
return command.help();
}
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)) {
throw Error(`${PKG.name} requires Node.js version ${nodeVersion} or higher.`);
}
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)) {
throw Error(
`${PKG.name} requires Node.js version ${nodeVersion} or higher.`
);
}
}
// --config
const loadedConfig = await loadConfig(opts.config);
if (loadedConfig != null) {
config = loadedConfig;
}
// --config
const loadedConfig = await loadConfig(opts.config);
if (loadedConfig != null) {
config = loadedConfig;
}
// --quiet
if (opts.quiet) {
config.quiet = opts.quiet;
}
// --quiet
if (opts.quiet) {
config.quiet = opts.quiet;
}
// --recursive
if (opts.recursive) {
config.recursive = opts.recursive;
}
// --recursive
if (opts.recursive) {
config.recursive = opts.recursive;
}
// --precision
if (opts.precision != null) {
var precision = Math.min(Math.max(0, opts.precision), 20);
config.floatPrecision = precision;
}
// --precision
if (opts.precision != null) {
var precision = Math.min(Math.max(0, opts.precision), 20);
config.floatPrecision = precision;
}
// --multipass
if (opts.multipass) {
config.multipass = true;
}
// --multipass
if (opts.multipass) {
config.multipass = true;
}
// --pretty
if (opts.pretty) {
config.js2svg = config.js2svg || {};
config.js2svg.pretty = true;
if (opts.indent != null) {
config.js2svg.indent = opts.indent;
}
// --pretty
if (opts.pretty) {
config.js2svg = config.js2svg || {};
config.js2svg.pretty = true;
if (opts.indent != null) {
config.js2svg.indent = opts.indent;
}
}
// --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));
}
// --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 (input.length) {
output = input;
} else if (opts.string) {
output = '-';
} else if (output.length < input.length) {
output = output.concat(input.slice(output.length));
}
}
} else if (input.length) {
output = input;
} else if (opts.string) {
output = '-';
}
if (opts.datauri) {
config.datauri = opts.datauri;
}
if (opts.datauri) {
config.datauri = opts.datauri;
}
// --folder
if (opts.folder) {
var ouputFolder = output && output[0] || opts.folder;
await optimizeFolder(config, opts.folder, ouputFolder);
}
// --folder
if (opts.folder) {
var ouputFolder = (output && output[0]) || opts.folder;
await optimizeFolder(config, opts.folder, ouputFolder);
}
// --input
if (input.length !== 0) {
// STDIN
if (input[0] === '-') {
return new Promise((resolve, reject) => {
var data = '',
file = output[0];
// --input
if (input.length !== 0) {
// 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 {
await Promise.all(
input.map((file, n) => optimizeFile(config, file, output[n]))
);
}
process.stdin
.on('data', (chunk) => (data += chunk))
.once('end', () =>
processSVGData(config, { input: 'string' }, data, file).then(
resolve,
reject
)
);
});
// file
} else {
await Promise.all(
input.map((file, n) => optimizeFile(config, file, output[n]))
);
}
// --string
} else if (opts.string) {
var data = decodeSVGDatauri(opts.string);
} else if (opts.string) {
var data = decodeSVGDatauri(opts.string);
return processSVGData(config, {input: 'string'}, data, output[0]);
}
return processSVGData(config, { input: 'string' }, data, output[0]);
}
}

@@ -204,6 +250,8 @@

function optimizeFolder(config, dir, output) {
if (!config.quiet) {
console.log(`Processing directory '${dir}':\n`);
}
return FS.promises.readdir(dir).then(files => processDirectory(config, dir, files, output));
if (!config.quiet) {
console.log(`Processing directory '${dir}':\n`);
}
return FS.promises
.readdir(dir)
.then((files) => processDirectory(config, dir, files, output));
}

@@ -220,8 +268,18 @@

function processDirectory(config, dir, files, output) {
// take only *.svg files, recursively if necessary
var svgFilesDescriptions = getFilesDescriptions(config, dir, files, output);
// 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.`));
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.`)
);
}

@@ -238,23 +296,28 @@

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),
}));
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;
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;
}

@@ -270,6 +333,7 @@

function optimizeFile(config, file, output) {
return FS.promises.readFile(file, 'utf8').then(
data => processSVGData(config, {input: 'file', path: file}, data, output, file),
error => checkOptimizeFileError(config, file, output, error)
);
return FS.promises.readFile(file, 'utf8').then(
(data) =>
processSVGData(config, { input: 'file', path: file }, data, output, file),
(error) => checkOptimizeFileError(config, file, output, error)
);
}

@@ -286,29 +350,38 @@

function processSVGData(config, info, data, output, input) {
var startTime = Date.now(),
prevFileSize = Buffer.byteLength(data, 'utf8');
var startTime = Date.now(),
prevFileSize = Buffer.byteLength(data, 'utf8');
const result = optimize(data, { ...config, ...info });
if (result.error) {
let message = result.error;
if (result.path != null) {
message += `\nFile: ${result.path}`
}
throw Error(message)
const result = optimize(data, { ...config, ...info });
if (result.error) {
let message = result.error;
if (result.path != null) {
message += `\nFile: ${result.path}`;
}
if (config.datauri) {
result.data = encodeSVGDatauri(result.data, config.datauri);
}
var resultFileSize = Buffer.byteLength(result.data, 'utf8'),
processingTime = Date.now() - startTime;
throw Error(message);
}
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)}:`);
}
printTimeInfo(processingTime);
printProfitInfo(prevFileSize, resultFileSize);
}
},
error => Promise.reject(new Error(error.code === 'ENOTDIR' ? `Error: output '${output}' is not a directory.` : error)));
(error) =>
Promise.reject(
new Error(
error.code === 'ENOTDIR'
? `Error: output '${output}' is not a directory.`
: error
)
)
);
}

@@ -324,13 +397,14 @@

function writeOutput(input, output, data) {
if (output == '-') {
console.log(data);
return Promise.resolve();
}
if (output == '-') {
console.log(data);
return Promise.resolve();
}
FS.mkdirSync(PATH.dirname(output), { recursive: true });
FS.mkdirSync(PATH.dirname(output), { recursive: true });
return FS.promises.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));
}
/**

@@ -341,3 +415,3 @@ * Write a time taken by optimization.

function printTimeInfo(time) {
console.log(`Done in ${time} ms!`);
console.log(`Done in ${time} ms!`);
}

@@ -351,10 +425,13 @@

function printProfitInfo(inBytes, outBytes) {
var profitPercents = 100 - outBytes * 100 / inBytes;
var profitPercents = 100 - (outBytes * 100) / inBytes;
console.log(
(Math.round((inBytes / 1024) * 1000) / 1000) + ' KiB' +
(profitPercents < 0 ? ' + ' : ' - ') +
chalk.green(Math.abs((Math.round(profitPercents * 10) / 10)) + '%') + ' = ' +
(Math.round((outBytes / 1024) * 1000) / 1000) + ' KiB'
);
console.log(
Math.round((inBytes / 1024) * 1000) / 1000 +
' KiB' +
(profitPercents < 0 ? ' + ' : ' - ') +
chalk.green(Math.abs(Math.round(profitPercents * 10) / 10) + '%') +
' = ' +
Math.round((outBytes / 1024) * 1000) / 1000 +
' KiB'
);
}

@@ -371,8 +448,10 @@

function checkOptimizeFileError(config, input, output, error) {
if (error.code == 'EISDIR') {
return optimizeFolder(config, input, output);
} else if (error.code == 'ENOENT') {
return Promise.reject(new Error(`Error: no such file or directory '${error.path}'.`));
}
return Promise.reject(error);
if (error.code == 'EISDIR') {
return optimizeFolder(config, input, output);
} else if (error.code == 'ENOENT') {
return Promise.reject(
new Error(`Error: no such file or directory '${error.path}'.`)
);
}
return Promise.reject(error);
}

@@ -389,7 +468,11 @@

function checkWriteFileError(input, output, data, error) {
if (error.code == 'EISDIR' && input) {
return FS.promises.writeFile(PATH.resolve(output, PATH.basename(input)), data, 'utf8');
} else {
return Promise.reject(error);
}
if (error.code == 'EISDIR' && input) {
return FS.promises.writeFile(
PATH.resolve(output, PATH.basename(input)),
data,
'utf8'
);
} else {
return Promise.reject(error);
}
}

@@ -401,9 +484,9 @@

function showAvailablePlugins() {
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);
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);
}
module.exports.checkIsDir = checkIsDir;

@@ -56,7 +56,10 @@ 'use strict';

];
const defaultPlugins = pluginsOrder.filter(name => pluginsMap[name].active);
const defaultPlugins = pluginsOrder.filter((name) => pluginsMap[name].active);
exports.defaultPlugins = defaultPlugins;
const extendDefaultPlugins = (plugins) => {
const extendedPlugins = pluginsOrder.map(name => ({ name, ...pluginsMap[name] }));
const extendedPlugins = pluginsOrder.map((name) => ({
name,
...pluginsMap[name],
}));
for (const plugin of plugins) {

@@ -72,3 +75,3 @@ const resolvedPlugin = resolvePluginConfig(plugin, {});

return extendedPlugins;
}
};
exports.extendDefaultPlugins = extendDefaultPlugins;

@@ -91,3 +94,3 @@

active: true,
params: { ...pluginConfig.params, ...configParams }
params: { ...pluginConfig.params, ...configParams },
};

@@ -104,3 +107,3 @@ }

...plugin,
params: { configParams, ...plugin.params }
params: { configParams, ...plugin.params },
};

@@ -117,3 +120,3 @@ } else {

...plugin,
params: { ...pluginConfig.params, ...configParams, ...plugin.params }
params: { ...pluginConfig.params, ...configParams, ...plugin.params },
};

@@ -120,0 +123,0 @@ }

'use strict';
var CSSClassList = function(node) {
this.parentNode = node;
this.classNames = new Set();
this.classAttr = null;
//this.classValue = null;
var CSSClassList = function (node) {
this.parentNode = node;
this.classNames = new Set();
this.classAttr = null;
//this.classValue = null;
};

@@ -15,42 +15,41 @@

*/
CSSClassList.prototype.clone = function(parentNode) {
var node = this;
var nodeData = {};
CSSClassList.prototype.clone = function (parentNode) {
var node = this;
var nodeData = {};
Object.keys(node).forEach(function(key) {
if (key !== 'parentNode') {
nodeData[key] = node[key];
}
});
Object.keys(node).forEach(function (key) {
if (key !== 'parentNode') {
nodeData[key] = node[key];
}
});
// Deep-clone node data.
nodeData = JSON.parse(JSON.stringify(nodeData));
// Deep-clone node data.
nodeData = JSON.parse(JSON.stringify(nodeData));
var clone = new CSSClassList(parentNode);
Object.assign(clone, nodeData);
return clone;
var clone = new CSSClassList(parentNode);
Object.assign(clone, nodeData);
return clone;
};
CSSClassList.prototype.hasClass = function() {
this.classAttr = { // empty class attr
'name': 'class',
'value': null
};
CSSClassList.prototype.hasClass = function () {
this.classAttr = {
// empty class attr
name: 'class',
value: null,
};
this.addClassHandler();
this.addClassHandler();
};
// attr.class
CSSClassList.prototype.addClassHandler = function() {
CSSClassList.prototype.addClassHandler = function () {
Object.defineProperty(this.parentNode.attrs, 'class', {
get: this.getClassAttr.bind(this),
set: this.setClassAttr.bind(this),
enumerable: true,
configurable: true,
});
Object.defineProperty(this.parentNode.attrs, 'class', {
get: this.getClassAttr.bind(this),
set: this.setClassAttr.bind(this),
enumerable: true,
configurable: true
});
this.addClassValueHandler();
this.addClassValueHandler();
};

@@ -60,75 +59,70 @@

CSSClassList.prototype.addClassValueHandler = function() {
Object.defineProperty(this.classAttr, 'value', {
get: this.getClassValue.bind(this),
set: this.setClassValue.bind(this),
enumerable: true,
configurable: true
});
CSSClassList.prototype.addClassValueHandler = function () {
Object.defineProperty(this.classAttr, 'value', {
get: this.getClassValue.bind(this),
set: this.setClassValue.bind(this),
enumerable: true,
configurable: true,
});
};
CSSClassList.prototype.getClassAttr = function() {
return this.classAttr;
CSSClassList.prototype.getClassAttr = function () {
return this.classAttr;
};
CSSClassList.prototype.setClassAttr = function(newClassAttr) {
this.setClassValue(newClassAttr.value); // must before applying value handler!
CSSClassList.prototype.setClassAttr = function (newClassAttr) {
this.setClassValue(newClassAttr.value); // must before applying value handler!
this.classAttr = newClassAttr;
this.addClassValueHandler();
this.classAttr = newClassAttr;
this.addClassValueHandler();
};
CSSClassList.prototype.getClassValue = function() {
var arrClassNames = Array.from(this.classNames);
return arrClassNames.join(' ');
CSSClassList.prototype.getClassValue = function () {
var arrClassNames = Array.from(this.classNames);
return arrClassNames.join(' ');
};
CSSClassList.prototype.setClassValue = function(newValue) {
if(typeof newValue === 'undefined') {
this.classNames.clear();
return;
}
var arrClassNames = newValue.split(' ');
this.classNames = new Set(arrClassNames);
CSSClassList.prototype.setClassValue = function (newValue) {
if (typeof newValue === 'undefined') {
this.classNames.clear();
return;
}
var arrClassNames = newValue.split(' ');
this.classNames = new Set(arrClassNames);
};
CSSClassList.prototype.add = function(/* variadic */) {
this.hasClass();
Object.values(arguments).forEach(this._addSingle.bind(this));
CSSClassList.prototype.add = function (/* variadic */) {
this.hasClass();
Object.values(arguments).forEach(this._addSingle.bind(this));
};
CSSClassList.prototype._addSingle = function(className) {
this.classNames.add(className);
CSSClassList.prototype._addSingle = function (className) {
this.classNames.add(className);
};
CSSClassList.prototype.remove = function(/* variadic */) {
this.hasClass();
Object.values(arguments).forEach(this._removeSingle.bind(this));
CSSClassList.prototype.remove = function (/* variadic */) {
this.hasClass();
Object.values(arguments).forEach(this._removeSingle.bind(this));
};
CSSClassList.prototype._removeSingle = function(className) {
this.classNames.delete(className);
CSSClassList.prototype._removeSingle = function (className) {
this.classNames.delete(className);
};
CSSClassList.prototype.item = function(index) {
var arrClassNames = Array.from(this.classNames);
return arrClassNames[index];
CSSClassList.prototype.item = function (index) {
var arrClassNames = Array.from(this.classNames);
return arrClassNames[index];
};
CSSClassList.prototype.toggle = function(className, force) {
if(this.contains(className) || force === false) {
this.classNames.delete(className);
}
this.classNames.add(className);
CSSClassList.prototype.toggle = function (className, force) {
if (this.contains(className) || force === false) {
this.classNames.delete(className);
}
this.classNames.add(className);
};
CSSClassList.prototype.contains = function(className) {
return this.classNames.has(className);
CSSClassList.prototype.contains = function (className) {
return this.classNames.has(className);
};
module.exports = CSSClassList;

@@ -9,41 +9,40 @@ 'use strict';

var svgoCssSelectAdapterMin = {
// is the node a tag?
// isTag: ( node:Node ) => isTag:Boolean
isTag: function (node) {
return node.isElem();
},
// is the node a tag?
// isTag: ( node:Node ) => isTag:Boolean
isTag: function(node) {
return node.isElem();
},
// get the parent of the node
// getParent: ( node:Node ) => parentNode:Node
// returns null when no parent exists
getParent: function (node) {
return node.parentNode || null;
},
// get the parent of the node
// getParent: ( node:Node ) => parentNode:Node
// returns null when no parent exists
getParent: function(node) {
return node.parentNode || null;
},
// get the node's children
// getChildren: ( node:Node ) => children:[Node]
getChildren: function (node) {
return node.content || [];
},
// get the node's children
// getChildren: ( node:Node ) => children:[Node]
getChildren: function(node) {
return node.content || [];
},
// get the name of the tag
// getName: ( elem:ElementNode ) => tagName:String
getName: function (elemAst) {
return elemAst.elem;
},
// get the name of the tag
// getName: ( elem:ElementNode ) => tagName:String
getName: function(elemAst) {
return elemAst.elem;
},
// get the text content of the node, and its children if it has any
// getText: ( node:Node ) => text:String
// returns empty string when there is no text
getText: function (node) {
return node.content[0].text || node.content[0].cdata || '';
},
// get the text content of the node, and its children if it has any
// getText: ( node:Node ) => text:String
// returns empty string when there is no text
getText: function(node) {
return node.content[0].text || node.content[0].cdata || '';
},
// get the attribute value
// getAttributeValue: ( elem:ElementNode, name:String ) => value:String
// returns null when attribute doesn't exist
getAttributeValue: function(elem, name) {
return elem.hasAttr(name) ? elem.attr(name).value : null;
}
// get the attribute value
// getAttributeValue: ( elem:ElementNode, name:String ) => value:String
// returns null when attribute doesn't exist
getAttributeValue: function (elem, name) {
return elem.hasAttr(name) ? elem.attr(name).value : null;
},
};

@@ -50,0 +49,0 @@

'use strict';
var csstree = require('css-tree'),
csstools = require('../css-tools');
csstools = require('../css-tools');
var CSSStyleDeclaration = function (node) {
this.parentNode = node;
var CSSStyleDeclaration = function(node) {
this.parentNode = node;
this.properties = new Map();
this.hasSynced = false;
this.properties = new Map();
this.hasSynced = false;
this.styleAttr = null;
this.styleValue = null;
this.styleAttr = null;
this.styleValue = null;
this.parseError = false;
this.parseError = false;
};

@@ -24,44 +23,41 @@

*/
CSSStyleDeclaration.prototype.clone = function(parentNode) {
var node = this;
var nodeData = {};
CSSStyleDeclaration.prototype.clone = function (parentNode) {
var node = this;
var nodeData = {};
Object.keys(node).forEach(function(key) {
if (key !== 'parentNode') {
nodeData[key] = node[key];
}
});
Object.keys(node).forEach(function (key) {
if (key !== 'parentNode') {
nodeData[key] = node[key];
}
});
// Deep-clone node data.
nodeData = JSON.parse(JSON.stringify(nodeData));
// Deep-clone node data.
nodeData = JSON.parse(JSON.stringify(nodeData));
var clone = new CSSStyleDeclaration(parentNode);
Object.assign(clone, nodeData);
return clone;
var clone = new CSSStyleDeclaration(parentNode);
Object.assign(clone, nodeData);
return clone;
};
CSSStyleDeclaration.prototype.hasStyle = function() {
this.addStyleHandler();
CSSStyleDeclaration.prototype.hasStyle = function () {
this.addStyleHandler();
};
// attr.style
CSSStyleDeclaration.prototype.addStyleHandler = function() {
CSSStyleDeclaration.prototype.addStyleHandler = function () {
this.styleAttr = {
// empty style attr
name: 'style',
value: null,
};
this.styleAttr = { // empty style attr
'name': 'style',
'value': null
};
Object.defineProperty(this.parentNode.attrs, 'style', {
get: this.getStyleAttr.bind(this),
set: this.setStyleAttr.bind(this),
enumerable: true,
configurable: true,
});
Object.defineProperty(this.parentNode.attrs, 'style', {
get: this.getStyleAttr.bind(this),
set: this.setStyleAttr.bind(this),
enumerable: true,
configurable: true
});
this.addStyleValueHandler();
this.addStyleValueHandler();
};

@@ -71,74 +67,73 @@

CSSStyleDeclaration.prototype.addStyleValueHandler = function() {
Object.defineProperty(this.styleAttr, 'value', {
get: this.getStyleValue.bind(this),
set: this.setStyleValue.bind(this),
enumerable: true,
configurable: true
});
CSSStyleDeclaration.prototype.addStyleValueHandler = function () {
Object.defineProperty(this.styleAttr, 'value', {
get: this.getStyleValue.bind(this),
set: this.setStyleValue.bind(this),
enumerable: true,
configurable: true,
});
};
CSSStyleDeclaration.prototype.getStyleAttr = function() {
return this.styleAttr;
CSSStyleDeclaration.prototype.getStyleAttr = function () {
return this.styleAttr;
};
CSSStyleDeclaration.prototype.setStyleAttr = function(newStyleAttr) {
this.setStyleValue(newStyleAttr.value); // must before applying value handler!
CSSStyleDeclaration.prototype.setStyleAttr = function (newStyleAttr) {
this.setStyleValue(newStyleAttr.value); // must before applying value handler!
this.styleAttr = newStyleAttr;
this.addStyleValueHandler();
this.hasSynced = false; // raw css changed
this.styleAttr = newStyleAttr;
this.addStyleValueHandler();
this.hasSynced = false; // raw css changed
};
CSSStyleDeclaration.prototype.getStyleValue = function() {
return this.getCssText();
CSSStyleDeclaration.prototype.getStyleValue = function () {
return this.getCssText();
};
CSSStyleDeclaration.prototype.setStyleValue = function(newValue) {
this.properties.clear(); // reset all existing properties
this.styleValue = newValue;
this.hasSynced = false; // raw css changed
CSSStyleDeclaration.prototype.setStyleValue = function (newValue) {
this.properties.clear(); // reset all existing properties
this.styleValue = newValue;
this.hasSynced = false; // raw css changed
};
CSSStyleDeclaration.prototype._loadCssText = function () {
if (this.hasSynced) {
return;
}
this.hasSynced = true; // must be set here to prevent loop in setProperty(...)
if (!this.styleValue || this.styleValue.length === 0) {
return;
}
var inlineCssStr = this.styleValue;
var declarations = {};
try {
declarations = csstree.parse(inlineCssStr, {
context: 'declarationList',
parseValue: false,
});
} catch (parseError) {
this.parseError = parseError;
return;
}
this.parseError = false;
CSSStyleDeclaration.prototype._loadCssText = function() {
if (this.hasSynced) {
return;
}
this.hasSynced = true; // must be set here to prevent loop in setProperty(...)
if (!this.styleValue || this.styleValue.length === 0) {
return;
}
var inlineCssStr = this.styleValue;
var declarations = {};
var self = this;
declarations.children.each(function (declaration) {
try {
declarations = csstree.parse(inlineCssStr, {
context: 'declarationList',
parseValue: false
});
} catch (parseError) {
this.parseError = parseError;
return;
var styleDeclaration = csstools.csstreeToStyleDeclaration(declaration);
self.setProperty(
styleDeclaration.name,
styleDeclaration.value,
styleDeclaration.priority
);
} catch (styleError) {
if (styleError.message !== 'Unknown node type: undefined') {
self.parseError = styleError;
}
}
this.parseError = false;
var self = this;
declarations.children.each(function(declaration) {
try {
var styleDeclaration = csstools.csstreeToStyleDeclaration(declaration);
self.setProperty(styleDeclaration.name, styleDeclaration.value, styleDeclaration.priority);
} catch(styleError) {
if(styleError.message !== 'Unknown node type: undefined') {
self.parseError = styleError;
}
}
});
});
};
// only reads from properties

@@ -151,35 +146,39 @@

*/
CSSStyleDeclaration.prototype.getCssText = function() {
var properties = this.getProperties();
CSSStyleDeclaration.prototype.getCssText = function () {
var properties = this.getProperties();
if (this.parseError) {
// in case of a parse error, pass through original styles
return this.styleValue;
}
if (this.parseError) {
// in case of a parse error, pass through original styles
return this.styleValue;
}
var cssText = [];
properties.forEach(function(property, propertyName) {
var strImportant = property.priority === 'important' ? '!important' : '';
cssText.push(propertyName.trim() + ':' + property.value.trim() + strImportant);
});
return cssText.join(';');
var cssText = [];
properties.forEach(function (property, propertyName) {
var strImportant = property.priority === 'important' ? '!important' : '';
cssText.push(
propertyName.trim() + ':' + property.value.trim() + strImportant
);
});
return cssText.join(';');
};
CSSStyleDeclaration.prototype._handleParseError = function() {
if (this.parseError) {
console.warn('Warning: Parse error when parsing inline styles, style properties of this element cannot be used. The raw styles can still be get/set using .attr(\'style\').value. Error details: ' + this.parseError);
}
CSSStyleDeclaration.prototype._handleParseError = function () {
if (this.parseError) {
console.warn(
"Warning: Parse error when parsing inline styles, style properties of this element cannot be used. The raw styles can still be get/set using .attr('style').value. Error details: " +
this.parseError
);
}
};
CSSStyleDeclaration.prototype._getProperty = function (propertyName) {
if (typeof propertyName === 'undefined') {
throw Error('1 argument required, but only 0 present.');
}
CSSStyleDeclaration.prototype._getProperty = function(propertyName) {
if(typeof propertyName === 'undefined') {
throw Error('1 argument required, but only 0 present.');
}
var properties = this.getProperties();
this._handleParseError();
var properties = this.getProperties();
this._handleParseError();
var property = properties.get(propertyName.trim());
return property;
var property = properties.get(propertyName.trim());
return property;
};

@@ -193,5 +192,5 @@

*/
CSSStyleDeclaration.prototype.getPropertyPriority = function(propertyName) {
var property = this._getProperty(propertyName);
return property ? property.priority : '';
CSSStyleDeclaration.prototype.getPropertyPriority = function (propertyName) {
var property = this._getProperty(propertyName);
return property ? property.priority : '';
};

@@ -205,5 +204,5 @@

*/
CSSStyleDeclaration.prototype.getPropertyValue = function(propertyName) {
var property = this._getProperty(propertyName);
return property ? property.value : null;
CSSStyleDeclaration.prototype.getPropertyValue = function (propertyName) {
var property = this._getProperty(propertyName);
return property ? property.value : null;
};

@@ -217,11 +216,11 @@

*/
CSSStyleDeclaration.prototype.item = function(index) {
if(typeof index === 'undefined') {
throw Error('1 argument required, but only 0 present.');
}
CSSStyleDeclaration.prototype.item = function (index) {
if (typeof index === 'undefined') {
throw Error('1 argument required, but only 0 present.');
}
var properties = this.getProperties();
this._handleParseError();
var properties = this.getProperties();
this._handleParseError();
return Array.from(properties.keys())[index];
return Array.from(properties.keys())[index];
};

@@ -234,8 +233,7 @@

*/
CSSStyleDeclaration.prototype.getProperties = function() {
this._loadCssText();
return this.properties;
CSSStyleDeclaration.prototype.getProperties = function () {
this._loadCssText();
return this.properties;
};
// writes to properties

@@ -249,15 +247,15 @@

*/
CSSStyleDeclaration.prototype.removeProperty = function(propertyName) {
if(typeof propertyName === 'undefined') {
throw Error('1 argument required, but only 0 present.');
}
CSSStyleDeclaration.prototype.removeProperty = function (propertyName) {
if (typeof propertyName === 'undefined') {
throw Error('1 argument required, but only 0 present.');
}
this.hasStyle();
this.hasStyle();
var properties = this.getProperties();
this._handleParseError();
var properties = this.getProperties();
this._handleParseError();
var oldValue = this.getPropertyValue(propertyName);
properties.delete(propertyName.trim());
return oldValue;
var oldValue = this.getPropertyValue(propertyName);
properties.delete(propertyName.trim());
return oldValue;
};

@@ -273,22 +271,25 @@

*/
CSSStyleDeclaration.prototype.setProperty = function(propertyName, value, priority) {
if(typeof propertyName === 'undefined') {
throw Error('propertyName argument required, but only not present.');
}
CSSStyleDeclaration.prototype.setProperty = function (
propertyName,
value,
priority
) {
if (typeof propertyName === 'undefined') {
throw Error('propertyName argument required, but only not present.');
}
this.hasStyle();
this.hasStyle();
var properties = this.getProperties();
this._handleParseError();
var properties = this.getProperties();
this._handleParseError();
var property = {
value: value.trim(),
priority: priority.trim()
};
properties.set(propertyName.trim(), property);
var property = {
value: value.trim(),
priority: priority.trim(),
};
properties.set(propertyName.trim(), property);
return property;
return property;
};
module.exports = CSSStyleDeclaration;
'use strict';
var EOL = require('os').EOL,
textElems = require('../../plugins/_collections.js').textElems;
textElems = require('../../plugins/_collections.js').textElems;
var defaults = {
doctypeStart: '<!DOCTYPE',
doctypeEnd: '>',
procInstStart: '<?',
procInstEnd: '?>',
tagOpenStart: '<',
tagOpenEnd: '>',
tagCloseStart: '</',
tagCloseEnd: '>',
tagShortStart: '<',
tagShortEnd: '/>',
attrStart: '="',
attrEnd: '"',
commentStart: '<!--',
commentEnd: '-->',
cdataStart: '<![CDATA[',
cdataEnd: ']]>',
textStart: '',
textEnd: '',
indent: 4,
regEntities: /[&'"<>]/g,
regValEntities: /[&"<>]/g,
encodeEntity: encodeEntity,
pretty: false,
useShortTags: true
doctypeStart: '<!DOCTYPE',
doctypeEnd: '>',
procInstStart: '<?',
procInstEnd: '?>',
tagOpenStart: '<',
tagOpenEnd: '>',
tagCloseStart: '</',
tagCloseEnd: '>',
tagShortStart: '<',
tagShortEnd: '/>',
attrStart: '="',
attrEnd: '"',
commentStart: '<!--',
commentEnd: '-->',
cdataStart: '<![CDATA[',
cdataEnd: ']]>',
textStart: '',
textEnd: '',
indent: 4,
regEntities: /[&'"<>]/g,
regValEntities: /[&"<>]/g,
encodeEntity: encodeEntity,
pretty: false,
useShortTags: true,
};
var entities = {
'&': '&amp;',
'\'': '&apos;',
'"': '&quot;',
'>': '&gt;',
'<': '&lt;',
};
'&': '&amp;',
"'": '&apos;',
'"': '&quot;',
'>': '&gt;',
'<': '&lt;',
};

@@ -49,41 +49,37 @@ /**

*/
module.exports = function(data, config) {
return new JS2SVG(config).convert(data);
module.exports = function (data, config) {
return new JS2SVG(config).convert(data);
};
function JS2SVG(config) {
if (config) {
this.config = Object.assign({}, defaults, config);
} else {
this.config = Object.assign({}, defaults);
}
if (config) {
this.config = Object.assign({}, defaults, config);
} else {
this.config = Object.assign({}, defaults);
}
var indent = this.config.indent;
if (typeof indent == 'number' && !isNaN(indent)) {
this.config.indent = indent < 0 ? '\t' : ' '.repeat(indent);
} else if (typeof indent != 'string') {
this.config.indent = ' ';
}
var indent = this.config.indent;
if (typeof indent == 'number' && !isNaN(indent)) {
this.config.indent = (indent < 0) ? '\t' : ' '.repeat(indent);
} else if (typeof indent != 'string') {
this.config.indent = ' ';
}
if (this.config.pretty) {
this.config.doctypeEnd += EOL;
this.config.procInstEnd += EOL;
this.config.commentEnd += EOL;
this.config.cdataEnd += EOL;
this.config.tagShortEnd += EOL;
this.config.tagOpenEnd += EOL;
this.config.tagCloseEnd += EOL;
this.config.textEnd += EOL;
}
if (this.config.pretty) {
this.config.doctypeEnd += EOL;
this.config.procInstEnd += EOL;
this.config.commentEnd += EOL;
this.config.cdataEnd += EOL;
this.config.tagShortEnd += EOL;
this.config.tagOpenEnd += EOL;
this.config.tagCloseEnd += EOL;
this.config.textEnd += EOL;
}
this.indentLevel = 0;
this.textContext = null;
this.indentLevel = 0;
this.textContext = null;
}
function encodeEntity(char) {
return entities[char];
return entities[char];
}

@@ -98,40 +94,34 @@

*/
JS2SVG.prototype.convert = function(data) {
JS2SVG.prototype.convert = function (data) {
var svg = '';
var svg = '';
if (data.content) {
this.indentLevel++;
if (data.content) {
data.content.forEach(function (item) {
if (item.elem) {
svg += this.createElem(item);
} else if (item.text) {
svg += this.createText(item.text);
} else if (item.doctype) {
svg += this.createDoctype(item.doctype);
} else if (item.processinginstruction) {
svg += this.createProcInst(item.processinginstruction);
} else if (item.comment) {
svg += this.createComment(item.comment);
} else if (item.cdata) {
svg += this.createCDATA(item.cdata);
}
}, this);
}
this.indentLevel++;
this.indentLevel--;
data.content.forEach(function(item) {
if (item.elem) {
svg += this.createElem(item);
} else if (item.text) {
svg += this.createText(item.text);
} else if (item.doctype) {
svg += this.createDoctype(item.doctype);
} else if (item.processinginstruction) {
svg += this.createProcInst(item.processinginstruction);
} else if (item.comment) {
svg += this.createComment(item.comment);
} else if (item.cdata) {
svg += this.createCDATA(item.cdata);
}
}, this);
}
this.indentLevel--;
return {
data: svg,
info: {
width: this.width,
height: this.height
}
};
return {
data: svg,
info: {
width: this.width,
height: this.height,
},
};
};

@@ -144,12 +134,10 @@

*/
JS2SVG.prototype.createIndent = function() {
JS2SVG.prototype.createIndent = function () {
var indent = '';
var indent = '';
if (this.config.pretty && !this.textContext) {
indent = this.config.indent.repeat(this.indentLevel - 1);
}
if (this.config.pretty && !this.textContext) {
indent = this.config.indent.repeat(this.indentLevel - 1);
}
return indent;
return indent;
};

@@ -164,8 +152,4 @@

*/
JS2SVG.prototype.createDoctype = function(doctype) {
return this.config.doctypeStart +
doctype +
this.config.doctypeEnd;
JS2SVG.prototype.createDoctype = function (doctype) {
return this.config.doctypeStart + doctype + this.config.doctypeEnd;
};

@@ -180,10 +164,10 @@

*/
JS2SVG.prototype.createProcInst = function(instruction) {
return this.config.procInstStart +
instruction.name +
' ' +
instruction.body +
this.config.procInstEnd;
JS2SVG.prototype.createProcInst = function (instruction) {
return (
this.config.procInstStart +
instruction.name +
' ' +
instruction.body +
this.config.procInstEnd
);
};

@@ -198,8 +182,4 @@

*/
JS2SVG.prototype.createComment = function(comment) {
return this.config.commentStart +
comment +
this.config.commentEnd;
JS2SVG.prototype.createComment = function (comment) {
return this.config.commentStart + comment + this.config.commentEnd;
};

@@ -214,9 +194,6 @@

*/
JS2SVG.prototype.createCDATA = function(cdata) {
return this.createIndent() +
this.config.cdataStart +
cdata +
this.config.cdataEnd;
JS2SVG.prototype.createCDATA = function (cdata) {
return (
this.createIndent() + this.config.cdataStart + cdata + this.config.cdataEnd
);
};

@@ -231,76 +208,75 @@

*/
JS2SVG.prototype.createElem = function(data) {
JS2SVG.prototype.createElem = function (data) {
// beautiful injection for obtaining SVG information :)
if (data.isElem('svg') && data.hasAttr('width') && data.hasAttr('height')) {
this.width = data.attr('width').value;
this.height = data.attr('height').value;
}
// beautiful injection for obtaining SVG information :)
if (
data.isElem('svg') &&
data.hasAttr('width') &&
data.hasAttr('height')
) {
this.width = data.attr('width').value;
this.height = data.attr('height').value;
// empty element and short tag
if (data.isEmpty()) {
if (this.config.useShortTags) {
return (
this.createIndent() +
this.config.tagShortStart +
data.elem +
this.createAttrs(data) +
this.config.tagShortEnd
);
} else {
return (
this.createIndent() +
this.config.tagShortStart +
data.elem +
this.createAttrs(data) +
this.config.tagOpenEnd +
this.config.tagCloseStart +
data.elem +
this.config.tagCloseEnd
);
}
// empty element and short tag
if (data.isEmpty()) {
if (this.config.useShortTags) {
return this.createIndent() +
this.config.tagShortStart +
data.elem +
this.createAttrs(data) +
this.config.tagShortEnd;
} else {
return this.createIndent() +
this.config.tagShortStart +
data.elem +
this.createAttrs(data) +
this.config.tagOpenEnd +
this.config.tagCloseStart +
data.elem +
this.config.tagCloseEnd;
}
// non-empty element
} else {
var tagOpenStart = this.config.tagOpenStart,
tagOpenEnd = this.config.tagOpenEnd,
tagCloseStart = this.config.tagCloseStart,
tagCloseEnd = this.config.tagCloseEnd,
openIndent = this.createIndent(),
closeIndent = this.createIndent(),
processedData = '',
dataEnd = '';
} else {
var tagOpenStart = this.config.tagOpenStart,
tagOpenEnd = this.config.tagOpenEnd,
tagCloseStart = this.config.tagCloseStart,
tagCloseEnd = this.config.tagCloseEnd,
openIndent = this.createIndent(),
closeIndent = this.createIndent(),
processedData = '',
dataEnd = '';
if (this.textContext) {
tagOpenStart = defaults.tagOpenStart;
tagOpenEnd = defaults.tagOpenEnd;
tagCloseStart = defaults.tagCloseStart;
tagCloseEnd = defaults.tagCloseEnd;
openIndent = '';
} else if (data.isElem(textElems)) {
tagOpenEnd = defaults.tagOpenEnd;
tagCloseStart = defaults.tagCloseStart;
closeIndent = '';
this.textContext = data;
}
if (this.textContext) {
tagOpenStart = defaults.tagOpenStart;
tagOpenEnd = defaults.tagOpenEnd;
tagCloseStart = defaults.tagCloseStart;
tagCloseEnd = defaults.tagCloseEnd;
openIndent = '';
} else if (data.isElem(textElems)) {
tagOpenEnd = defaults.tagOpenEnd;
tagCloseStart = defaults.tagCloseStart;
closeIndent = '';
this.textContext = data;
}
processedData += this.convert(data).data;
processedData += this.convert(data).data;
if (this.textContext == data) {
this.textContext = null;
}
return openIndent +
tagOpenStart +
data.elem +
this.createAttrs(data) +
tagOpenEnd +
processedData +
dataEnd +
closeIndent +
tagCloseStart +
data.elem +
tagCloseEnd;
if (this.textContext == data) {
this.textContext = null;
}
return (
openIndent +
tagOpenStart +
data.elem +
this.createAttrs(data) +
tagOpenEnd +
processedData +
dataEnd +
closeIndent +
tagCloseStart +
data.elem +
tagCloseEnd
);
}
};

@@ -315,25 +291,22 @@

*/
JS2SVG.prototype.createAttrs = function(elem) {
JS2SVG.prototype.createAttrs = function (elem) {
var attrs = '';
var attrs = '';
elem.eachAttr(function (attr) {
if (attr.value !== undefined) {
attrs +=
' ' +
attr.name +
this.config.attrStart +
String(attr.value).replace(
this.config.regValEntities,
this.config.encodeEntity
) +
this.config.attrEnd;
} else {
attrs += ' ' + attr.name;
}
}, this);
elem.eachAttr(function(attr) {
if (attr.value !== undefined) {
attrs += ' ' +
attr.name +
this.config.attrStart +
String(attr.value).replace(this.config.regValEntities, this.config.encodeEntity) +
this.config.attrEnd;
}
else {
attrs += ' ' +
attr.name;
}
}, this);
return attrs;
return attrs;
};

@@ -348,9 +321,9 @@

*/
JS2SVG.prototype.createText = function(text) {
return this.createIndent() +
this.config.textStart +
text.replace(this.config.regEntities, this.config.encodeEntity) +
(this.textContext ? '' : this.config.textEnd);
JS2SVG.prototype.createText = function (text) {
return (
this.createIndent() +
this.config.textStart +
text.replace(this.config.regEntities, this.config.encodeEntity) +
(this.textContext ? '' : this.config.textEnd)
);
};

@@ -8,14 +8,15 @@ 'use strict';

xmlMode: true,
adapter: svgoCssSelectAdapter
adapter: svgoCssSelectAdapter,
};
var JSAPI = module.exports = function(data, parentNode) {
Object.assign(this, data);
if (parentNode) {
Object.defineProperty(this, 'parentNode', {
writable: true,
value: parentNode
});
}
var JSAPI = function (data, parentNode) {
Object.assign(this, data);
if (parentNode) {
Object.defineProperty(this, 'parentNode', {
writable: true,
value: parentNode,
});
}
};
module.exports = JSAPI;

@@ -27,35 +28,35 @@ /**

*/
JSAPI.prototype.clone = function() {
var node = this;
var nodeData = {};
JSAPI.prototype.clone = function () {
var node = this;
var nodeData = {};
Object.keys(node).forEach(function(key) {
if (key !== 'class' && key !== 'style' && key !== 'content') {
nodeData[key] = node[key];
}
});
Object.keys(node).forEach(function (key) {
if (key !== 'class' && key !== 'style' && key !== 'content') {
nodeData[key] = node[key];
}
});
// Deep-clone node data.
nodeData = JSON.parse(JSON.stringify(nodeData));
// Deep-clone node data.
nodeData = JSON.parse(JSON.stringify(nodeData));
// parentNode gets set to a proper object by the parent clone,
// but it needs to be true/false now to do the right thing
// in the constructor.
var clonedNode = new JSAPI(nodeData, !!node.parentNode);
// parentNode gets set to a proper object by the parent clone,
// but it needs to be true/false now to do the right thing
// in the constructor.
var clonedNode = new JSAPI(nodeData, !!node.parentNode);
if (node.class) {
clonedNode.class = node.class.clone(clonedNode);
}
if (node.style) {
clonedNode.style = node.style.clone(clonedNode);
}
if (node.content) {
clonedNode.content = node.content.map(function(childNode) {
var clonedChild = childNode.clone();
clonedChild.parentNode = clonedNode;
return clonedChild;
});
}
if (node.class) {
clonedNode.class = node.class.clone(clonedNode);
}
if (node.style) {
clonedNode.style = node.style.clone(clonedNode);
}
if (node.content) {
clonedNode.content = node.content.map(function (childNode) {
var clonedChild = childNode.clone();
clonedChild.parentNode = clonedNode;
return clonedChild;
});
}
return clonedNode;
return clonedNode;
};

@@ -70,10 +71,8 @@

*/
JSAPI.prototype.isElem = function(param) {
JSAPI.prototype.isElem = function (param) {
if (!param) return !!this.elem;
if (!param) return !!this.elem;
if (Array.isArray(param)) return !!this.elem && param.indexOf(this.elem) > -1;
if (Array.isArray(param)) return !!this.elem && (param.indexOf(this.elem) > -1);
return !!this.elem && this.elem === param;
return !!this.elem && this.elem === param;
};

@@ -87,9 +86,6 @@

*/
JSAPI.prototype.renameElem = function(name) {
JSAPI.prototype.renameElem = function (name) {
if (name && typeof name === 'string') this.elem = this.local = name;
if (name && typeof name === 'string')
this.elem = this.local = name;
return this;
return this;
};

@@ -102,6 +98,4 @@

*/
JSAPI.prototype.isEmpty = function() {
return !this.content || !this.content.length;
JSAPI.prototype.isEmpty = function () {
return !this.content || !this.content.length;
};

@@ -115,8 +109,8 @@

*/
JSAPI.prototype.closestElem = function(elemName) {
var elem = this;
JSAPI.prototype.closestElem = function (elemName) {
var elem = this;
while ((elem = elem.parentNode) && !elem.isElem(elemName));
while ((elem = elem.parentNode) && !elem.isElem(elemName));
return elem;
return elem;
};

@@ -132,14 +126,13 @@

*/
JSAPI.prototype.spliceContent = function(start, n, insertion) {
JSAPI.prototype.spliceContent = function (start, n, insertion) {
if (arguments.length < 2) return [];
if (arguments.length < 2) return [];
if (!Array.isArray(insertion))
insertion = Array.apply(null, arguments).slice(2);
if (!Array.isArray(insertion))
insertion = Array.apply(null, arguments).slice(2);
insertion.forEach(function (inner) {
inner.parentNode = this;
}, this);
insertion.forEach(function(inner) { inner.parentNode = this }, this);
return this.content.splice.apply(this.content, [start, n].concat(insertion));
return this.content.splice.apply(this.content, [start, n].concat(insertion));
};

@@ -155,12 +148,11 @@

*/
JSAPI.prototype.hasAttr = function(name, val) {
JSAPI.prototype.hasAttr = function (name, val) {
if (!this.attrs || !Object.keys(this.attrs).length) return false;
if (!this.attrs || !Object.keys(this.attrs).length) return false;
if (!arguments.length) return !!this.attrs;
if (!arguments.length) return !!this.attrs;
if (val !== undefined)
return !!this.attrs[name] && this.attrs[name].value === val.toString();
if (val !== undefined) return !!this.attrs[name] && this.attrs[name].value === val.toString();
return !!this.attrs[name];
return !!this.attrs[name];
};

@@ -176,35 +168,40 @@

*/
JSAPI.prototype.hasAttrLocal = function(localName, val) {
JSAPI.prototype.hasAttrLocal = function (localName, val) {
if (!this.attrs || !Object.keys(this.attrs).length) return false;
if (!this.attrs || !Object.keys(this.attrs).length) return false;
if (!arguments.length) return !!this.attrs;
if (!arguments.length) return !!this.attrs;
var callback;
var callback;
switch (val != null && val.constructor && val.constructor.name) {
case 'Number': // same as String
case 'String':
callback = stringValueTest;
break;
case 'RegExp':
callback = regexpValueTest;
break;
case 'Function':
callback = funcValueTest;
break;
default:
callback = nameTest;
}
return this.someAttr(callback);
switch (val != null && val.constructor && val.constructor.name) {
case 'Number': // same as String
case 'String': callback = stringValueTest; break;
case 'RegExp': callback = regexpValueTest; break;
case 'Function': callback = funcValueTest; break;
default: callback = nameTest;
}
return this.someAttr(callback);
function nameTest(attr) {
return attr.local === localName;
}
function nameTest(attr) {
return attr.local === localName;
}
function stringValueTest(attr) {
return attr.local === localName && val == attr.value;
}
function stringValueTest(attr) {
return attr.local === localName && val == attr.value;
}
function regexpValueTest(attr) {
return attr.local === localName && val.test(attr.value);
}
function regexpValueTest(attr) {
return attr.local === localName && val.test(attr.value);
}
function funcValueTest(attr) {
return attr.local === localName && val(attr.value);
}
function funcValueTest(attr) {
return attr.local === localName && val(attr.value);
}
};

@@ -220,10 +217,9 @@

*/
JSAPI.prototype.attr = function(name, val) {
JSAPI.prototype.attr = function (name, val) {
if (!this.hasAttr() || !arguments.length) return undefined;
if (!this.hasAttr() || !arguments.length) return undefined;
if (val !== undefined)
return this.hasAttr(name, val) ? this.attrs[name] : undefined;
if (val !== undefined) return this.hasAttr(name, val) ? this.attrs[name] : undefined;
return this.attrs[name];
return this.attrs[name];
};

@@ -237,13 +233,16 @@

*/
JSAPI.prototype.computedAttr = function(name, val) {
if (!arguments.length) return;
JSAPI.prototype.computedAttr = function (name, val) {
if (!arguments.length) return;
for (var elem = this; elem && (!elem.hasAttr(name) || !elem.attr(name).value); elem = elem.parentNode);
for (
var elem = this;
elem && (!elem.hasAttr(name) || !elem.attr(name).value);
elem = elem.parentNode
);
if (val != null) {
return elem ? elem.hasAttr(name, val) : false;
} else if (elem && elem.hasAttr(name)) {
return elem.attrs[name].value;
}
if (val != null) {
return elem ? elem.hasAttr(name, val) : false;
} else if (elem && elem.hasAttr(name)) {
return elem.attrs[name].value;
}
};

@@ -258,21 +257,19 @@

*/
JSAPI.prototype.removeAttr = function(name, val, recursive) {
JSAPI.prototype.removeAttr = function (name, val, recursive) {
if (!arguments.length) return false;
if (!arguments.length) return false;
if (Array.isArray(name)) {
name.forEach(this.removeAttr, this);
return false;
}
if (Array.isArray(name)) {
name.forEach(this.removeAttr, this);
return false;
}
if (!this.hasAttr(name)) return false;
if (!this.hasAttr(name)) return false;
if (!recursive && val && this.attrs[name].value !== val) return false;
if (!recursive && val && this.attrs[name].value !== val) return false;
delete this.attrs[name];
delete this.attrs[name];
if (!Object.keys(this.attrs).length) delete this.attrs;
if (!Object.keys(this.attrs).length) delete this.attrs;
return true;
return true;
};

@@ -286,23 +283,26 @@

*/
JSAPI.prototype.addAttr = function(attr) {
attr = attr || {};
JSAPI.prototype.addAttr = function (attr) {
attr = attr || {};
if (attr.name === undefined ||
attr.prefix === undefined ||
attr.local === undefined
) return false;
if (
attr.name === undefined ||
attr.prefix === undefined ||
attr.local === undefined
)
return false;
this.attrs = this.attrs || {};
this.attrs[attr.name] = attr;
this.attrs = this.attrs || {};
this.attrs[attr.name] = attr;
if(attr.name === 'class') { // newly added class attribute
this.class.hasClass();
}
if (attr.name === 'class') {
// newly added class attribute
this.class.hasClass();
}
if(attr.name === 'style') { // newly added style attribute
this.style.hasStyle();
}
if (attr.name === 'style') {
// newly added style attribute
this.style.hasStyle();
}
return this.attrs[attr.name];
return this.attrs[attr.name];
};

@@ -317,12 +317,10 @@

*/
JSAPI.prototype.eachAttr = function(callback, context) {
JSAPI.prototype.eachAttr = function (callback, context) {
if (!this.hasAttr()) return false;
if (!this.hasAttr()) return false;
for (const attr of Object.values(this.attrs)) {
callback.call(context, attr);
}
for (var name in this.attrs) {
callback.call(context, this.attrs[name]);
}
return true;
return true;
};

@@ -337,12 +335,10 @@

*/
JSAPI.prototype.someAttr = function(callback, context) {
JSAPI.prototype.someAttr = function (callback, context) {
if (!this.hasAttr()) return false;
if (!this.hasAttr()) return false;
for (const attr of Object.values(this.attrs)) {
if (callback.call(context, attr)) return true;
}
for (var name in this.attrs) {
if (callback.call(context, this.attrs[name])) return true;
}
return false;
return false;
};

@@ -356,8 +352,6 @@

*/
JSAPI.prototype.querySelectorAll = function(selectors) {
JSAPI.prototype.querySelectorAll = function (selectors) {
var matchedEls = selectAll(selectors, this, cssSelectOpts);
var matchedEls = selectAll(selectors, this, cssSelectOpts);
return matchedEls.length > 0 ? matchedEls : null;
return matchedEls.length > 0 ? matchedEls : null;
};

@@ -371,6 +365,4 @@

*/
JSAPI.prototype.querySelector = function(selectors) {
return selectOne(selectors, this, cssSelectOpts);
JSAPI.prototype.querySelector = function (selectors) {
return selectOne(selectors, this, cssSelectOpts);
};

@@ -384,6 +376,4 @@

*/
JSAPI.prototype.matches = function(selector) {
return is(this, selector, cssSelectOpts);
JSAPI.prototype.matches = function (selector) {
return is(this, selector, cssSelectOpts);
};

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

*/
module.exports = function(data, info, plugins) {
module.exports = function (data, info, plugins) {
// Try to group sequential elements of plugins array

@@ -28,3 +28,3 @@ // to optimize ast traversing

for (const group of groups) {
switch(group[0].type) {
switch (group[0].type) {
case 'perItem':

@@ -54,38 +54,32 @@ data = perItem(data, info, group);

function perItem(data, info, plugins, reverse) {
function monkeys(items) {
items.content = items.content.filter(function (item) {
// reverse pass
if (reverse && item.content) {
monkeys(item);
}
function monkeys(items) {
// main filter
var filter = true;
items.content = items.content.filter(function(item) {
for (var i = 0; filter && i < plugins.length; i++) {
var plugin = plugins[i];
// reverse pass
if (reverse && item.content) {
monkeys(item);
}
if (plugin.active && plugin.fn(item, plugin.params, info) === false) {
filter = false;
}
}
// main filter
var filter = true;
// direct pass
if (!reverse && item.content) {
monkeys(item);
}
for (var i = 0; filter && i < plugins.length; i++) {
var plugin = plugins[i];
return filter;
});
if (plugin.active && plugin.fn(item, plugin.params, info) === false) {
filter = false;
}
}
return items;
}
// direct pass
if (!reverse && item.content) {
monkeys(item);
}
return filter;
});
return items;
}
return monkeys(data);
return monkeys(data);
}

@@ -102,11 +96,9 @@

function full(data, info, plugins) {
plugins.forEach(function (plugin) {
if (plugin.active) {
data = plugin.fn(data, plugin.params, info);
}
});
plugins.forEach(function(plugin) {
if (plugin.active) {
data = plugin.fn(data, plugin.params, info);
}
});
return data;
return data;
}
'use strict';
var SAX = require('@trysound/sax'),
JSAPI = require('./jsAPI.js'),
CSSClassList = require('./css-class-list'),
CSSStyleDeclaration = require('./css-style-declaration'),
textElems = require('../../plugins/_collections.js').textElems,
entityDeclaration = /<!ENTITY\s+(\S+)\s+(?:'([^']+)'|"([^"]+)")\s*>/g;
JSAPI = require('./jsAPI.js'),
CSSClassList = require('./css-class-list'),
CSSStyleDeclaration = require('./css-style-declaration'),
textElems = require('../../plugins/_collections.js').textElems,
entityDeclaration = /<!ENTITY\s+(\S+)\s+(?:'([^']+)'|"([^"]+)")\s*>/g;
var config = {
strict: true,
trim: false,
normalize: false,
lowercase: true,
xmlns: true,
position: true
strict: true,
trim: false,
normalize: false,
lowercase: true,
xmlns: true,
position: true,
};

@@ -24,130 +24,116 @@

*/
module.exports = function(data) {
module.exports = function (data) {
var sax = SAX.parser(config.strict, config),
root = new JSAPI({ elem: '#document', content: [] }),
current = root,
stack = [root];
var sax = SAX.parser(config.strict, config),
root = new JSAPI({ elem: '#document', content: [] }),
current = root,
stack = [root];
function pushToContent(content) {
content = new JSAPI(content, current);
function pushToContent(content) {
(current.content = current.content || []).push(content);
content = new JSAPI(content, current);
return content;
}
(current.content = current.content || []).push(content);
sax.ondoctype = function (doctype) {
pushToContent({
doctype: doctype,
});
return content;
var subsetStart = doctype.indexOf('['),
entityMatch;
if (subsetStart >= 0) {
entityDeclaration.lastIndex = subsetStart;
while ((entityMatch = entityDeclaration.exec(data)) != null) {
sax.ENTITIES[entityMatch[1]] = entityMatch[2] || entityMatch[3];
}
}
};
sax.ondoctype = function(doctype) {
sax.onprocessinginstruction = function (data) {
pushToContent({
processinginstruction: data,
});
};
pushToContent({
doctype: doctype
});
sax.oncomment = function (comment) {
pushToContent({
comment: comment.trim(),
});
};
var subsetStart = doctype.indexOf('['),
entityMatch;
sax.oncdata = function (cdata) {
pushToContent({
cdata: cdata,
});
};
if (subsetStart >= 0) {
entityDeclaration.lastIndex = subsetStart;
while ((entityMatch = entityDeclaration.exec(data)) != null) {
sax.ENTITIES[entityMatch[1]] = entityMatch[2] || entityMatch[3];
}
}
sax.onopentag = function (data) {
var elem = {
elem: data.name,
prefix: data.prefix,
local: data.local,
attrs: {},
};
sax.onprocessinginstruction = function(data) {
elem.class = new CSSClassList(elem);
elem.style = new CSSStyleDeclaration(elem);
pushToContent({
processinginstruction: data
});
if (Object.keys(data.attributes).length) {
for (const [name, attr] of Object.entries(data.attributes)) {
if (name === 'class') {
// has class attribute
elem.class.hasClass();
}
};
if (name === 'style') {
// has style attribute
elem.style.hasStyle();
}
sax.oncomment = function(comment) {
pushToContent({
comment: comment.trim()
});
};
sax.oncdata = function(cdata) {
pushToContent({
cdata: cdata
});
};
sax.onopentag = function(data) {
var elem = {
elem: data.name,
prefix: data.prefix,
local: data.local,
attrs: {}
elem.attrs[name] = {
name: name,
value: attr.value,
prefix: attr.prefix,
local: attr.local,
};
elem.class = new CSSClassList(elem);
elem.style = new CSSStyleDeclaration(elem);
if (Object.keys(data.attributes).length) {
for (var name in data.attributes) {
if (name === 'class') { // has class attribute
elem.class.hasClass();
}
if (name === 'style') { // has style attribute
elem.style.hasStyle();
}
elem.attrs[name] = {
name: name,
value: data.attributes[name].value,
prefix: data.attributes[name].prefix,
local: data.attributes[name].local
};
}
}
elem = pushToContent(elem);
current = elem;
stack.push(elem);
};
sax.ontext = function(text) {
// prevent trimming of meaningful whitespace inside textual tags
if (textElems.includes(current.elem) && !data.prefix) {
pushToContent({ text: text });
} else if (/\S/.test(text)) {
pushToContent({ text: text.trim() });
}
};
}
sax.onclosetag = function() {
stack.pop();
current = stack[stack.length - 1];
};
elem = pushToContent(elem);
current = elem;
sax.onerror = function(e) {
stack.push(elem);
};
e.message = 'Error in parsing SVG: ' + e.message;
if (e.message.indexOf('Unexpected end') < 0) {
throw e;
}
sax.ontext = function (text) {
// prevent trimming of meaningful whitespace inside textual tags
if (textElems.includes(current.elem) && !data.prefix) {
pushToContent({ text: text });
} else if (/\S/.test(text)) {
pushToContent({ text: text.trim() });
}
};
};
sax.onclosetag = function () {
stack.pop();
current = stack[stack.length - 1];
};
try {
sax.write(data).close();
return root;
} catch (e) {
return { error: e.message };
sax.onerror = function (e) {
e.message = 'Error in parsing SVG: ' + e.message;
if (e.message.indexOf('Unexpected end') < 0) {
throw e;
}
};
try {
sax.write(data).close();
return root;
} catch (e) {
return { error: e.message };
}
};

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

*/
exports.encodeSVGDatauri = function(str, type) {
var prefix = 'data:image/svg+xml';
if (!type || type === 'base64') {
// base64
prefix += ';base64,';
str = prefix + Buffer.from(str).toString('base64');
} else if (type === 'enc') {
// URI encoded
str = prefix + ',' + encodeURIComponent(str);
} else if (type === 'unenc') {
// unencoded
str = prefix + ',' + str;
}
return str;
exports.encodeSVGDatauri = function (str, type) {
var prefix = 'data:image/svg+xml';
if (!type || type === 'base64') {
// base64
prefix += ';base64,';
str = prefix + Buffer.from(str).toString('base64');
} else if (type === 'enc') {
// URI encoded
str = prefix + ',' + encodeURIComponent(str);
} else if (type === 'unenc') {
// unencoded
str = prefix + ',' + str;
}
return str;
};

@@ -33,28 +33,28 @@

*/
exports.decodeSVGDatauri = function(str) {
var regexp = /data:image\/svg\+xml(;charset=[^;,]*)?(;base64)?,(.*)/;
var match = regexp.exec(str);
exports.decodeSVGDatauri = function (str) {
var regexp = /data:image\/svg\+xml(;charset=[^;,]*)?(;base64)?,(.*)/;
var match = regexp.exec(str);
// plain string
if (!match) return str;
// plain string
if (!match) return str;
var data = match[3];
var data = match[3];
if (match[2]) {
// base64
str = Buffer.from(data, 'base64').toString('utf8');
} else if (data.charAt(0) === '%') {
// URI encoded
str = decodeURIComponent(data);
} else if (data.charAt(0) === '<') {
// unencoded
str = data;
}
return str;
if (match[2]) {
// base64
str = Buffer.from(data, 'base64').toString('utf8');
} else if (data.charAt(0) === '%') {
// URI encoded
str = decodeURIComponent(data);
} else if (data.charAt(0) === '<') {
// unencoded
str = data;
}
return str;
};
exports.intersectArrays = function(a, b) {
return a.filter(function(n) {
return b.indexOf(n) > -1;
});
exports.intersectArrays = function (a, b) {
return a.filter(function (n) {
return b.indexOf(n) > -1;
});
};

@@ -73,44 +73,42 @@

*/
exports.cleanupOutData = function(data, params, command) {
var str = '',
delimiter,
prev;
exports.cleanupOutData = function (data, params, command) {
var str = '',
delimiter,
prev;
data.forEach(function(item, i) {
// space delimiter by default
delimiter = ' ';
data.forEach(function (item, i) {
// space delimiter by default
delimiter = ' ';
// no extra space in front of first number
if (i == 0) delimiter = '';
// no extra space in front of first number
if (i == 0) delimiter = '';
// no extra space after 'arcto' command flags(large-arc and sweep flags)
// a20 60 45 0 1 30 20 → a20 60 45 0130 20
if (params.noSpaceAfterFlags && (command == 'A' || command == 'a')) {
var pos = i % 7;
if (pos == 4 || pos == 5) delimiter = '';
}
// no extra space after 'arcto' command flags(large-arc and sweep flags)
// a20 60 45 0 1 30 20 → a20 60 45 0130 20
if (params.noSpaceAfterFlags && (command == 'A' || command == 'a')) {
var pos = i % 7;
if (pos == 4 || pos == 5) delimiter = '';
}
// remove floating-point numbers leading zeros
// 0.5 → .5
// -0.5 → -.5
if (params.leadingZero) {
item = removeLeadingZero(item);
}
// remove floating-point numbers leading zeros
// 0.5 → .5
// -0.5 → -.5
if (params.leadingZero) {
item = removeLeadingZero(item);
}
// no extra space in front of negative number or
// in front of a floating number if a previous number is floating too
if (
params.negativeExtraSpace &&
delimiter != '' &&
(item < 0 ||
(String(item).charCodeAt(0) == 46 && prev % 1 !== 0)
)
) {
delimiter = '';
}
// save prev item value
prev = item;
str += delimiter + item;
});
return str;
// no extra space in front of negative number or
// in front of a floating number if a previous number is floating too
if (
params.negativeExtraSpace &&
delimiter != '' &&
(item < 0 || (String(item).charCodeAt(0) == 46 && prev % 1 !== 0))
) {
delimiter = '';
}
// save prev item value
prev = item;
str += delimiter + item;
});
return str;
};

@@ -131,11 +129,12 @@

*/
var removeLeadingZero = exports.removeLeadingZero = function(num) {
var strNum = num.toString();
var removeLeadingZero = function (num) {
var strNum = num.toString();
if (0 < num && num < 1 && strNum.charCodeAt(0) == 48) {
strNum = strNum.slice(1);
} else if (-1 < num && num < 0 && strNum.charCodeAt(1) == 48) {
strNum = strNum.charAt(0) + strNum.slice(2);
}
return strNum;
if (0 < num && num < 1 && strNum.charCodeAt(0) == 48) {
strNum = strNum.slice(1);
} else if (-1 < num && num < 0 && strNum.charCodeAt(1) == 48) {
strNum = strNum.charAt(0) + strNum.slice(2);
}
return strNum;
};
exports.removeLeadingZero = removeLeadingZero;
{
"name": "svgo",
"version": "2.1.0",
"version": "2.2.0",
"description": "Nodejs-based tool for optimizing SVG vector graphics files",

@@ -24,3 +24,3 @@ "keywords": [

"email": "peimei@ya.ru",
"url": "http://github.com/arikon"
"url": "https://github.com/arikon"
},

@@ -30,3 +30,3 @@ {

"email": "lev.sun@ya.ru",
"url": "http://github.com/GreLI"
"url": "https://github.com/GreLI"
},

@@ -36,3 +36,3 @@ {

"email": "trysound@yandex.ru",
"url": "http://github.com/TrySound"
"url": "https://github.com/TrySound"
}

@@ -55,3 +55,3 @@ ],

"scripts": {
"test": "c8 --reporter=html --reporter=text mocha \"test/*/_index.js\"",
"test": "c8 --reporter=html --reporter=text mocha \"test/*/_index.js\" \"**/*.test.js\" --ignore=\"node_modules/**\"",
"lint": "eslint .",

@@ -61,2 +61,5 @@ "test-browser": "rollup -c && node ./test/browser.js",

},
"prettier": {
"singleQuote": true
},
"eslintConfig": {

@@ -88,3 +91,4 @@ "ignorePatterns": [

"files": [
"test/**/*.js"
"test/**/*.js",
"**/*.test.js"
],

@@ -118,2 +122,3 @@ "env": {

"playwright": "^1.8.1",
"prettier": "^2.2.1",
"rollup": "^2.39.0"

@@ -120,0 +125,0 @@ },

'use strict';
// http://www.w3.org/TR/SVG11/intro.html#Definitions
// https://www.w3.org/TR/SVG11/intro.html#Definitions
exports.elemsGroups = {

@@ -22,3 +22,3 @@ animation: ['animate', 'animateColor', 'animateMotion', 'animateTransform', 'set'],

// http://www.w3.org/TR/SVG11/intro.html#Definitions
// https://www.w3.org/TR/SVG11/intro.html#Definitions
exports.attrsGroups = {

@@ -162,3 +162,3 @@ animationAddition: ['additive', 'accumulate'],

// http://www.w3.org/TR/SVG11/eltindex.html
// https://www.w3.org/TR/SVG11/eltindex.html
exports.elems = {

@@ -208,3 +208,5 @@ a: {

'text',
'view'
'view',
// not spec compliant
'tspan',
]

@@ -2273,3 +2275,3 @@ },

// http://wiki.inkscape.org/wiki/index.php/Inkscape-specific_XML_attributes
// https://wiki.inkscape.org/wiki/index.php/Inkscape-specific_XML_attributes
exports.editorNamespaces = [

@@ -2300,3 +2302,3 @@ 'http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd',

// http://www.w3.org/TR/SVG11/linking.html#processingIRI
// https://www.w3.org/TR/SVG11/linking.html#processingIRI
exports.referencesProps = [

@@ -2315,3 +2317,3 @@ 'clip-path',

// http://www.w3.org/TR/SVG11/propidx.html
// https://www.w3.org/TR/SVG11/propidx.html
exports.inheritableAttrs = [

@@ -2377,3 +2379,3 @@ 'clip-rule',

// http://www.w3.org/TR/SVG11/single-page.html#types-ColorKeywords
// https://www.w3.org/TR/SVG11/single-page.html#types-ColorKeywords
exports.colorsNames = {

@@ -2565,5 +2567,5 @@ 'aliceblue': '#f0f8ff',

// http://www.w3.org/TR/SVG11/single-page.html#types-DataTypeColor
// https://www.w3.org/TR/SVG11/single-page.html#types-DataTypeColor
exports.colorsProps = [
'color', 'fill', 'stroke', 'stop-color', 'flood-color', 'lighting-color'
];
'use strict';
var rNumber = String.raw`[-+]?(?:\d*\.\d+|\d+\.?)(?:[eE][-+]?\d+)?\s*`,
rCommaWsp = String.raw`(?:\s,?\s*|,\s*)`,
rNumberCommaWsp = `(${rNumber})` + rCommaWsp,
rFlagCommaWsp = `([01])${rCommaWsp}?`,
rCoordinatePair = String.raw`(${rNumber})${rCommaWsp}?(${rNumber})`,
rArcSeq = (rNumberCommaWsp + '?').repeat(2) + rNumberCommaWsp + rFlagCommaWsp.repeat(2) + rCoordinatePair;
const { parsePathData, stringifyPathData } = require('../lib/path.js');
var regPathInstructions = /([MmLlHhVvCcSsQqTtAaZz])\s*/,
regCoordinateSequence = new RegExp(rNumber, 'g'),
regArcArgumentSequence = new RegExp(rArcSeq, 'g'),
regNumericValues = /[-+]?(\d*\.\d+|\d+\.?)(?:[eE][-+]?\d+)?/,
var regNumericValues = /[-+]?(\d*\.\d+|\d+\.?)(?:[eE][-+]?\d+)?/g,
transform2js = require('./_transforms').transform2js,

@@ -20,3 +12,2 @@ transformsMultiply = require('./_transforms').transformsMultiply,

defaultStrokeWidth = collections.attrsGroupsDefaults.presentation['stroke-width'],
cleanupOutData = require('../lib/svgo/tools').cleanupOutData,
removeLeadingZero = require('../lib/svgo/tools').removeLeadingZero,

@@ -33,73 +24,18 @@ prevCtrlPoint;

exports.path2js = function(path) {
if (path.pathJS) return path.pathJS;
var paramsLength = { // Number of parameters of every path command
H: 1, V: 1, M: 2, L: 2, T: 2, Q: 4, S: 4, C: 6, A: 7,
h: 1, v: 1, m: 2, l: 2, t: 2, q: 4, s: 4, c: 6, a: 7
},
pathData = [], // JS representation of the path data
instruction, // current instruction context
startMoveto = false;
// splitting path string into array like ['M', '10 50', 'L', '20 30']
path.attr('d').value.split(regPathInstructions).forEach(function(data) {
if (!data) return;
if (!startMoveto) {
if (data == 'M' || data == 'm') {
startMoveto = true;
} else return;
}
// instruction item
if (regPathInstructions.test(data)) {
instruction = data;
// z - instruction w/o data
if (instruction == 'Z' || instruction == 'z') {
pathData.push({
instruction: 'z'
});
}
// data item
} else {
if (instruction == 'A' || instruction == 'a') {
var newData = [];
for (var args; (args = regArcArgumentSequence.exec(data));) {
for (var i = 1; i < args.length; i++) {
newData.push(args[i]);
}
}
data = newData;
} else {
data = data.match(regCoordinateSequence);
}
if (!data) return;
data = data.map(Number);
// Subsequent moveto pairs of coordinates are threated as implicit lineto commands
// http://www.w3.org/TR/SVG/paths.html#PathDataMovetoCommands
if (instruction == 'M' || instruction == 'm') {
pathData.push({
instruction: pathData.length == 0 ? 'M' : instruction,
data: data.splice(0, 2)
});
instruction = instruction == 'M' ? 'L' : 'l';
}
for (var pair = paramsLength[instruction]; data.length;) {
pathData.push({
instruction: instruction,
data: data.splice(0, pair)
});
}
}
});
// First moveto is actually absolute. Subsequent coordinates were separated above.
if (pathData.length && pathData[0].instruction == 'm') {
pathData[0].instruction = 'M';
if (path.pathJS) return path.pathJS;
const pathData = []; // JS representation of the path data
const newPathData = parsePathData(path.attr('d').value);
for (const { command, args } of newPathData) {
if (command === 'Z' || command === 'z') {
pathData.push({ instruction: 'z' });
} else {
pathData.push({ instruction: command, data: args });
}
path.pathJS = pathData;
return pathData;
}
// First moveto is actually absolute. Subsequent coordinates were separated above.
if (pathData.length && pathData[0].instruction == 'm') {
pathData[0].instruction = 'M';
}
path.pathJS = pathData;
return pathData;
};

@@ -192,9 +128,16 @@

exports.applyTransforms = function(elem, path, params) {
// if there are no 'stroke' attr and references to other objects such as
// gradiends or clip-path which are also subjects to transform.
if (!elem.hasAttr('transform') || !elem.attr('transform').value ||
elem.someAttr(function(attr) {
return ~referencesProps.indexOf(attr.name) && ~attr.value.indexOf('url(');
}))
return path;
// if there are no 'stroke' attr and references to other objects such as
// gradiends or clip-path which are also subjects to transform.
if (
!elem.hasAttr('transform') ||
!elem.attr('transform').value ||
// styles are not considered when applying transform
// can be fixed properly with new style engine
elem.hasAttr('style') ||
elem.someAttr((attr) =>
referencesProps.includes(attr.name) && attr.value.includes('url(')
)
) {
return path;
}

@@ -246,2 +189,14 @@ var matrix = transformsMultiply(transform2js(elem.attr('transform').value)),

}
if (elem.hasAttr('stroke-dashoffset')) {
elem.attrs['stroke-dashoffset'].value = elem.attrs['stroke-dashoffset'].value
.trim()
.replace(regNumericValues, (num) => removeLeadingZero(num * scale));
}
if (elem.hasAttr('stroke-dasharray')) {
elem.attrs['stroke-dasharray'].value = elem.attrs['stroke-dasharray'].value
.trim()
.replace(regNumericValues, (num) => removeLeadingZero(num * scale));
}
}

@@ -349,3 +304,3 @@ }

*
* @see http://processingjs.nihongoresources.com/bezierinfo/
* @see https://pomax.github.io/bezierinfo/
*

@@ -457,3 +412,3 @@ * @param {Float} xa

*
* @see http://processingjs.nihongoresources.com/bezierinfo/
* @see https://pomax.github.io/bezierinfo/
*

@@ -553,60 +508,28 @@ * @param {Float} xa

path.pathJS = data;
path.pathJS = data;
if (params.collapseRepeated) {
data = collapseRepeated(data);
const pathData = [];
for (const item of data) {
// remove moveto commands which are followed by moveto commands
if (
pathData.length !== 0 &&
(item.instruction === 'M' || item.instruction === 'm')
) {
const last = pathData[pathData.length - 1];
if (last.command === 'M' || last.command === 'm') {
pathData.pop();
}
}
pathData.push({
command: item.instruction,
args: item.data || [],
});
}
path.attr('d').value = data.reduce(function(pathString, item) {
var strData = '';
if (item.data) {
strData = cleanupOutData(item.data, params, item.instruction);
}
return pathString += item.instruction + strData;
}, '');
path.attr('d').value = stringifyPathData({
pathData,
precision: params.floatPrecision,
});
};
/**
* Collapse repeated instructions data
*
* @param {Array} path input path data
* @return {Array} output path data
*/
function collapseRepeated(data) {
var prev,
prevIndex;
// copy an array and modifieds item to keep original data untouched
data = data.reduce(function(newPath, item) {
if (
prev && item.data &&
item.instruction == prev.instruction
) {
// concat previous data with current
if (item.instruction != 'M') {
prev = newPath[prevIndex] = {
instruction: prev.instruction,
data: prev.data.concat(item.data),
coords: item.coords,
base: prev.base
};
} else {
prev.data = item.data;
prev.coords = item.coords;
}
} else {
newPath.push(item);
prev = item;
prevIndex = newPath.length - 1;
}
return newPath;
}, []);
return data;
}
function set(dest, source) {

@@ -621,3 +544,3 @@ dest[0] = source[source.length - 2];

* collision using Gilbert-Johnson-Keerthi distance algorithm
* http://entropyinteractive.com/2011/04/gjk-algorithm/
* https://web.archive.org/web/20180822200027/http://entropyinteractive.com/2011/04/gjk-algorithm/
*

@@ -851,3 +774,3 @@ * @param {Array} path1 JS path representation

* Forms a convex hull from set of points of every subpath using monotone chain convex hull algorithm.
* http://en.wikibooks.org/wiki/Algorithm_Implementation/Geometry/Convex_hull/Monotone_chain
* https://en.wikibooks.org/wiki/Algorithm_Implementation/Geometry/Convex_hull/Monotone_chain
*

@@ -914,3 +837,3 @@ * @param points An array of [X, Y] coordinates

// for more information of where this Math came from visit:
// http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes
// https://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes
var _120 = Math.PI * 120 / 180,

@@ -917,0 +840,0 @@ rad = Math.PI / 180 * (+angle || 0),

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

* Decompose matrix into simple transforms. See
* http://frederic-wang.fr/decomposition-of-2d-transform-matrices.html
* https://frederic-wang.fr/decomposition-of-2d-transform-matrices.html
*

@@ -121,0 +121,0 @@ * @param {Object} data matrix transform object

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

*
* @see http://www.w3.org/TR/SVG/filters.html#EnableBackgroundProperty
* @see https://www.w3.org/TR/SVG11/filters.html#EnableBackgroundProperty
*

@@ -15,0 +15,0 @@ * @example

@@ -27,4 +27,4 @@ 'use strict';

*
* @see http://www.w3.org/TR/SVG/types.html#DataTypeColor
* @see http://www.w3.org/TR/SVG/single-page.html#types-ColorKeywords
* @see https://www.w3.org/TR/SVG11/types.html#DataTypeColor
* @see https://www.w3.org/TR/SVG11/single-page.html#types-ColorKeywords
*

@@ -31,0 +31,0 @@ * @example

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

*
* @see http://www.w3.org/TR/SVG/shapes.html
* @see https://www.w3.org/TR/SVG11/shapes.html
*

@@ -15,0 +15,0 @@ * @param {Object} item current iteration item

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

*
* @see http://www.w3.org/TR/SVG/paths.html#PathData
* @see https://www.w3.org/TR/SVG11/paths.html#PathData
*

@@ -73,5 +73,7 @@ * @param {Object} item current iteration item

var stroke = item.computedAttr('stroke'),
strokeLinecap = item.computedAttr('stroke');
hasStrokeLinecap = stroke && stroke != 'none' && strokeLinecap && strokeLinecap != 'butt';
const stroke = item.computedAttr('stroke');
const strokeLinecap = item.computedAttr('stroke-linecap');
// stroke-linecap may exist in inline styles which are not parsed for now
hasStrokeLinecap = item.hasAttr('style') ||
stroke && stroke != 'none' && strokeLinecap && strokeLinecap != 'butt';

@@ -78,0 +80,0 @@ var data = path2js(item);

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

*
* @see http://www.w3.org/TR/SVG/shapes.html
* @see https://www.w3.org/TR/SVG11/shapes.html
*

@@ -24,0 +24,0 @@ * @param {Object} item current iteration item

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

*
* @see http://www.w3.org/TR/SVG/coords.html#TransformMatrixDefined
* @see https://www.w3.org/TR/SVG11/coords.html#TransformMatrixDefined
*

@@ -41,0 +41,0 @@ * @param {Object} item current iteration item

@@ -57,9 +57,3 @@ 'use strict';

function cloneObject(obj) {
var result = {};
for (var key in obj) {
result[key] = obj[key];
}
return result;
return {...obj};
}

@@ -153,5 +147,5 @@

for (var key in rawData) {
for (const [key, data] of Object.entries(rawData)) {
if (shouldFilter(options, key)) {
usageData[key] = Object.keys(rawData[key]);
usageData[key] = Object.keys(data);
hasData = true;

@@ -158,0 +152,0 @@ }

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

for (var name in intersection) {
for (const [name, attr] of Object.entries(intersection)) {

@@ -76,5 +76,5 @@ if (!allPath && !hasClip || name !== 'transform') {

if (item.hasAttr('transform')) {
item.attr('transform').value += ' ' + intersection[name].value;
item.attr('transform').value += ' ' + attr.value;
} else {
item.addAttr(intersection[name]);
item.addAttr(attr);
}

@@ -85,3 +85,3 @@

} else {
item.addAttr(intersection[name]);
item.addAttr(attr);
}

@@ -112,3 +112,3 @@

for (var n in a) {
for (const [n, attr] of Object.entries(a)) {
if (

@@ -118,8 +118,8 @@ // eslint-disable-next-line no-prototype-builtins

inheritableAttrs.indexOf(n) > -1 &&
a[n].name === b[n].name &&
a[n].value === b[n].value &&
a[n].prefix === b[n].prefix &&
a[n].local === b[n].local
attr.name === b[n].name &&
attr.value === b[n].value &&
attr.prefix === b[n].prefix &&
attr.local === b[n].local
) {
c[n] = a[n];
c[n] = attr;
}

@@ -126,0 +126,0 @@ }

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

*
* @see http://www.w3.org/TR/SVG/intro.html#TermContainerElement
* @see https://www.w3.org/TR/SVG11/intro.html#TermContainerElement
*

@@ -36,4 +36,6 @@ * @example

// to be created and filled with pattern.
(item.isElem('g') && item.hasAttr('filter'))
(item.isElem('g') && item.hasAttr('filter')) ||
// empty <mask> hides masked element
(item.isElem('mask') && item.hasAttr('id'))
);
};

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

*
* @see http://www.w3.org/TR/SVG/text.html
* @see https://www.w3.org/TR/SVG11/text.html
*

@@ -21,0 +21,0 @@ * @example

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

params.isHidden &&
item.hasAttr('visibility', 'hidden')
item.hasAttr('visibility', 'hidden') &&
// keep if any descendant enables visibility
item.querySelector('[visibility=visible]') == null
) return false;

@@ -61,3 +63,3 @@

//
// http://www.w3.org/TR/SVG/painting.html#DisplayProperty
// https://www.w3.org/TR/SVG11/painting.html#DisplayProperty
// "A value of display: none indicates that the given element

@@ -72,6 +74,8 @@ // and its children shall not be rendered directly"

//
// http://www.w3.org/TR/SVG/masking.html#ObjectAndGroupOpacityProperties
// https://www.w3.org/TR/SVG11/masking.html#ObjectAndGroupOpacityProperties
if (
params.opacity0 &&
item.hasAttr('opacity', '0')
item.hasAttr('opacity', '0') &&
// transparent element inside clipPath still affect clipped elements
item.closestElem('clipPath') == null
) return false;

@@ -81,3 +85,3 @@

//
// http://www.w3.org/TR/SVG/shapes.html#CircleElementRAttribute
// https://www.w3.org/TR/SVG11/shapes.html#CircleElementRAttribute
// "A value of zero disables rendering of the element"

@@ -95,3 +99,3 @@ //

//
// http://www.w3.org/TR/SVG/shapes.html#EllipseElementRXAttribute
// https://www.w3.org/TR/SVG11/shapes.html#EllipseElementRXAttribute
// "A value of zero disables rendering of the element"

@@ -109,3 +113,3 @@ //

//
// http://www.w3.org/TR/SVG/shapes.html#EllipseElementRYAttribute
// https://www.w3.org/TR/SVG11/shapes.html#EllipseElementRYAttribute
// "A value of zero disables rendering of the element"

@@ -123,3 +127,3 @@ //

//
// http://www.w3.org/TR/SVG/shapes.html#RectElementWidthAttribute
// https://www.w3.org/TR/SVG11/shapes.html#RectElementWidthAttribute
// "A value of zero disables rendering of the element"

@@ -137,3 +141,3 @@ //

//
// http://www.w3.org/TR/SVG/shapes.html#RectElementHeightAttribute
// https://www.w3.org/TR/SVG11/shapes.html#RectElementHeightAttribute
// "A value of zero disables rendering of the element"

@@ -152,3 +156,3 @@ //

//
// http://www.w3.org/TR/SVG/pservers.html#PatternElementWidthAttribute
// https://www.w3.org/TR/SVG11/pservers.html#PatternElementWidthAttribute
// "A value of zero disables rendering of the element (i.e., no paint is applied)"

@@ -165,3 +169,3 @@ //

//
// http://www.w3.org/TR/SVG/pservers.html#PatternElementHeightAttribute
// https://www.w3.org/TR/SVG11/pservers.html#PatternElementHeightAttribute
// "A value of zero disables rendering of the element (i.e., no paint is applied)"

@@ -178,3 +182,3 @@ //

//
// http://www.w3.org/TR/SVG/struct.html#ImageElementWidthAttribute
// https://www.w3.org/TR/SVG11/struct.html#ImageElementWidthAttribute
// "A value of zero disables rendering of the element"

@@ -191,3 +195,3 @@ //

//
// http://www.w3.org/TR/SVG/struct.html#ImageElementHeightAttribute
// https://www.w3.org/TR/SVG11/struct.html#ImageElementHeightAttribute
// "A value of zero disables rendering of the element"

@@ -204,3 +208,3 @@ //

//
// http://www.w3.org/TR/SVG/paths.html#DAttribute
// https://www.w3.org/TR/SVG11/paths.html#DAttribute
//

@@ -216,3 +220,3 @@ // <path d=""/>

//
// http://www.w3.org/TR/SVG/shapes.html#PolylineElementPointsAttribute
// https://www.w3.org/TR/SVG11/shapes.html#PolylineElementPointsAttribute
//

@@ -228,3 +232,3 @@ // <polyline points="">

//
// http://www.w3.org/TR/SVG/shapes.html#PolygonElementPointsAttribute
// https://www.w3.org/TR/SVG11/shapes.html#PolygonElementPointsAttribute
//

@@ -231,0 +235,0 @@ // <polygon points="">

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

*
* http://www.w3.org/TR/SVG/metadata.html
* https://www.w3.org/TR/SVG11/metadata.html
*

@@ -15,0 +15,0 @@ * @param {Object} item current iteration item

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

*
* https://www.w3.org/TR/SVG/script.html
* https://www.w3.org/TR/SVG11/script.html
*

@@ -15,0 +15,0 @@ * @param {Object} item current iteration item

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

*
* http://www.w3.org/TR/SVG/styling.html#StyleElement
* https://www.w3.org/TR/SVG11/styling.html#StyleElement
*

@@ -15,0 +15,0 @@ * @param {Object} item current iteration item

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

// collect and extend all references
for (var elem in elems) {
elem = elems[elem];
for (const elem of Object.values(elems)) {
if (elem.attrsGroups) {

@@ -43,4 +41,4 @@ elem.attrs = elem.attrs || [];

for (var attrName in groupDefaults) {
elem.defaults[attrName] = groupDefaults[attrName];
for (const [attrName, attr] of Object.entries(groupDefaults)) {
elem.defaults[attrName] = attr;
}

@@ -47,0 +45,0 @@ }

@@ -44,23 +44,30 @@ 'use strict';

params.stroke &&
(!stroke ||
stroke == 'none' ||
item.computedAttr('stroke-opacity', '0') ||
item.computedAttr('stroke-width', '0')
(
!stroke ||
stroke == 'none' ||
item.computedAttr('stroke-opacity', '0') ||
item.computedAttr('stroke-width', '0')
)
) {
var parentStroke = item.parentNode.computedAttr('stroke'),
declineStroke = parentStroke && parentStroke != 'none';
// stroke-width may affect the size of marker-end
if (
item.computedAttr('stroke-width', '0') === true ||
item.computedAttr('marker-end') == null
) {
var parentStroke = item.parentNode.computedAttr('stroke'),
declineStroke = parentStroke && parentStroke != 'none';
item.eachAttr(function(attr) {
if (regStrokeProps.test(attr.name)) {
item.removeAttr(attr.name);
}
});
item.eachAttr(function(attr) {
if (regStrokeProps.test(attr.name)) {
item.removeAttr(attr.name);
}
});
if (declineStroke) item.addAttr({
name: 'stroke',
value: 'none',
prefix: '',
local: 'stroke'
});
if (declineStroke) item.addAttr({
name: 'stroke',
value: 'none',
prefix: '',
local: 'stroke'
});
}
}

@@ -67,0 +74,0 @@

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

*
* @see http://www.w3.org/TR/SVG/coords.html#ViewBoxAttribute
* @see https://www.w3.org/TR/SVG11/coords.html#ViewBoxAttribute
*

@@ -17,0 +17,0 @@ * @example

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

<img src="https://svg.github.io/svgo-logo.svg" width="200" height="200" alt="logo"/>
<div align="center">
<img src="./logo/logo-web.svg" width="348.61" height="100" alt="SVGO logo"/>
</div>
## SVGO [![NPM version](https://badge.fury.io/js/svgo.svg)](https://npmjs.org/package/svgo)
**SVG O**ptimizer is a Nodejs-based tool for optimizing SVG vector graphics files.
![](https://mc.yandex.ru/watch/18431326)
## SVGO [![npm version](https://img.shields.io/npm/v/svgo)](https://npmjs.org/package/svgo) [![Discord](https://img.shields.io/discord/815166721315831868)](https://discord.gg/z8jX8NYxrE)
**SVG O**ptimizer is a Node.js-based tool for optimizing SVG vector graphics files.
## Why?

@@ -17,3 +19,5 @@

```
or
```sh

@@ -29,3 +33,4 @@ yarn global add svgo

Or use --folder, -f flag to optimize whole folder of svg icons
Or use the `--folder`/`-f` flag to optimize a whole folder of SVG icons
```sh

@@ -36,2 +41,3 @@ svgo -f ./path/to/folder/with/svg/files -o ./path/to/folder/with/svg/output

See help for advanced usage
```sh

@@ -43,3 +49,3 @@ svgo --help

Some options can be configured with CLI though it may be easier to have configuration in separate file.
Some options can be configured with CLI though it may be easier to have the configuration in a separate file.
SVGO automatically loads configuration from `svgo.config.js` or module specified with `--config` flag.

@@ -59,3 +65,3 @@

SVGO has a plugin-based architecture, so almost every optimization is a separate plugin.
There is a set of [builtin plugins](#builtin-plugins). See how to configure them:
There is a set of [built-in plugins](#built-in-plugins). See how to configure them:

@@ -65,3 +71,3 @@ ```js

plugins: [
// enable builtin plugin by name
// enable a built-in plugin by name
'builtinPluginName',

@@ -83,4 +89,4 @@ // or by expanded version

If `plugins` field is specified default list is fully overrided. To extend default
list use `extendDefaultPlugins` utility:
The default list is fully overridden if the `plugins` field is specified. To extend the default
list use the `extendDefaultPlugins` utility:

@@ -101,3 +107,3 @@ ```js

To disable one of default plugins use `active` field:
To disable one of the default plugins use the `active` field:

@@ -116,3 +122,3 @@ ```js

See the list of default plugins:
See the list of the default plugins:

@@ -130,3 +136,2 @@ ```js

'minifyStyles',
'convertStyleToAttrs',
'cleanupIDs',

@@ -161,3 +166,3 @@ 'removeUselessDefs',

It's also possible to specify custom plugin:
It's also possible to specify a custom plugin:

@@ -202,3 +207,3 @@ ```js

If you write a tool on top of svgo you might need a way to load svgo config.
If you write a tool on top of SVGO you might need a way to load SVGO config.

@@ -209,7 +214,7 @@ ```js

// you can also specify relative or absolute path and customize current working directory
// you can also specify a relative or absolute path and customize the current working directory
const config = await loadConfig(configFile, cwd)
```
## Builtin plugins
## Built-in plugins

@@ -220,3 +225,3 @@ | Plugin | Description | Default |

| [inlineStyles](https://github.com/svg/svgo/blob/master/plugins/inlineStyles.js) | move and merge styles from `<style>` elements to element `style` attributes | `enabled` |
| [removeDoctype](https://github.com/svg/svgo/blob/master/plugins/removeDoctype.js) | remove doctype declaration | `enabled` |
| [removeDoctype](https://github.com/svg/svgo/blob/master/plugins/removeDoctype.js) | remove `doctype` declaration | `enabled` |
| [removeXMLProcInst](https://github.com/svg/svgo/blob/master/plugins/removeXMLProcInst.js) | remove XML processing instructions | `enabled` |

@@ -228,3 +233,3 @@ | [removeComments](https://github.com/svg/svgo/blob/master/plugins/removeComments.js) | remove comments | `enabled` |

| [removeUselessDefs](https://github.com/svg/svgo/blob/master/plugins/removeUselessDefs.js) | remove elements of `<defs>` without `id` | `enabled` |
| [removeXMLNS](https://github.com/svg/svgo/blob/master/plugins/removeXMLNS.js) | removes `xmlns` attribute (for inline svg) | `disabled` |
| [removeXMLNS](https://github.com/svg/svgo/blob/master/plugins/removeXMLNS.js) | removes the `xmlns` attribute (for inline SVG) | `disabled` |
| [removeEditorsNSData](https://github.com/svg/svgo/blob/master/plugins/removeEditorsNSData.js) | remove editors namespaces, elements, and attributes | `enabled` |

@@ -242,5 +247,5 @@ | [removeEmptyAttrs](https://github.com/svg/svgo/blob/master/plugins/removeEmptyAttrs.js) | remove empty attributes | `enabled` |

| [convertTransform](https://github.com/svg/svgo/blob/master/plugins/convertTransform.js) | collapse multiple transforms into one, convert matrices to the short aliases, and much more | `enabled` |
| [removeUnknownsAndDefaults](https://github.com/svg/svgo/blob/master/plugins/removeUnknownsAndDefaults.js) | remove unknown elements content and attributes, remove attrs with default values | `enabled` |
| [removeUnknownsAndDefaults](https://github.com/svg/svgo/blob/master/plugins/removeUnknownsAndDefaults.js) | remove unknown elements content and attributes, remove attributes with default values | `enabled` |
| [removeNonInheritableGroupAttrs](https://github.com/svg/svgo/blob/master/plugins/removeNonInheritableGroupAttrs.js) | remove non-inheritable group's "presentation" attributes | `enabled` |
| [removeUselessStrokeAndFill](https://github.com/svg/svgo/blob/master/plugins/removeUselessStrokeAndFill.js) | remove useless `stroke` and `fill` attrs | `enabled` |
| [removeUselessStrokeAndFill](https://github.com/svg/svgo/blob/master/plugins/removeUselessStrokeAndFill.js) | remove useless `stroke` and `fill` attributes | `enabled` |
| [removeUnusedNS](https://github.com/svg/svgo/blob/master/plugins/removeUnusedNS.js) | remove unused namespaces declaration | `enabled` |

@@ -262,5 +267,5 @@ | [prefixIds](https://github.com/svg/svgo/blob/master/plugins/prefixIds.js) | prefix IDs and classes with the SVG filename or an arbitrary string | `disabled` |

| [removeAttrs](https://github.com/svg/svgo/blob/master/plugins/removeAttrs.js) | remove attributes by pattern | `disabled` |
| [removeAttributesBySelector](https://github.com/svg/svgo/blob/master/plugins/removeAttributesBySelector.js) | removes attributes of elements that match a css selector | `disabled` |
| [removeElementsByAttr](https://github.com/svg/svgo/blob/master/plugins/removeElementsByAttr.js) | remove arbitrary elements by ID or className | `disabled` |
| [addClassesToSVGElement](https://github.com/svg/svgo/blob/master/plugins/addClassesToSVGElement.js) | add classnames to an outer `<svg>` element | `disabled` |
| [removeAttributesBySelector](https://github.com/svg/svgo/blob/master/plugins/removeAttributesBySelector.js) | removes attributes of elements that match a CSS selector | `disabled` |
| [removeElementsByAttr](https://github.com/svg/svgo/blob/master/plugins/removeElementsByAttr.js) | remove arbitrary elements by `ID` or `className` | `disabled` |
| [addClassesToSVGElement](https://github.com/svg/svgo/blob/master/plugins/addClassesToSVGElement.js) | add classnames to an outer `<svg>` element | `disabled` |
| [addAttributesToSVGElement](https://github.com/svg/svgo/blob/master/plugins/addAttributesToSVGElement.js) | adds attributes to an outer `<svg>` element | `disabled` |

@@ -276,3 +281,3 @@ | [removeOffCanvasPaths](https://github.com/svg/svgo/blob/master/plugins/removeOffCanvasPaths.js) | removes elements that are drawn outside of the viewbox | `disabled` |

* as a GitHub Action – [SVGO Action](https://github.com/marketplace/actions/svgo-action)
* as a Nodejs module – [examples](https://github.com/svg/svgo/tree/master/examples)
* as a Node.js module – [examples](https://github.com/svg/svgo/tree/master/examples)
* as a Grunt task – [grunt-svgmin](https://github.com/sindresorhus/grunt-svgmin)

@@ -294,12 +299,8 @@ * as a Gulp task – [gulp-svgmin](https://github.com/ben-eb/gulp-svgmin)

## Backers
## Donators
| [<img src="https://sheetjs.com/sketch128.png" width="80">](https://sheetjs.com/) | [<img src="https://rawgithub.com/fontello/fontello/master/fontello-image.svg" width="80">](http://fontello.com/) |
| [<img src="https://sheetjs.com/sketch128.png" width="80">](https://sheetjs.com/) | [<img src="https://raw.githubusercontent.com/fontello/fontello/master/fontello-image.svg" width="80">](https://fontello.com/) |
|:-:|:-:|
| [SheetJS LLC](https://sheetjs.com/) | [Fontello](http://fontello.com/) |
| [SheetJS LLC](https://sheetjs.com/) | [Fontello](https://fontello.com/) |
## Donations
- PayPal: https://www.paypal.me/deepsweet
## License and Copyright

@@ -309,2 +310,2 @@

Logo by [Yegor Bolshakov](http://xizzzy.ru/).
Logo by [André Castillo](https://github.com/DerianAndre).

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

SocketSocket SOC 2 Logo

Product

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

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc