Socket
Socket
Sign inDemoInstall

svgo

Package Overview
Dependencies
16
Maintainers
4
Versions
102
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 3.3.1 to 3.3.2

bin/svgo

164

lib/builtin.js

@@ -1,111 +0,57 @@

import presetDefault from '../plugins/preset-default.js';
import * as addAttributesToSVGElement from '../plugins/addAttributesToSVGElement.js';
import * as addClassesToSVGElement from '../plugins/addClassesToSVGElement.js';
import * as cleanupAttrs from '../plugins/cleanupAttrs.js';
import * as cleanupEnableBackground from '../plugins/cleanupEnableBackground.js';
import * as cleanupIds from '../plugins/cleanupIds.js';
import * as cleanupListOfValues from '../plugins/cleanupListOfValues.js';
import * as cleanupNumericValues from '../plugins/cleanupNumericValues.js';
import * as collapseGroups from '../plugins/collapseGroups.js';
import * as convertColors from '../plugins/convertColors.js';
import * as convertEllipseToCircle from '../plugins/convertEllipseToCircle.js';
import * as convertOneStopGradients from '../plugins/convertOneStopGradients.js';
import * as convertPathData from '../plugins/convertPathData.js';
import * as convertShapeToPath from '../plugins/convertShapeToPath.js';
import * as convertStyleToAttrs from '../plugins/convertStyleToAttrs.js';
import * as convertTransform from '../plugins/convertTransform.js';
import * as mergeStyles from '../plugins/mergeStyles.js';
import * as inlineStyles from '../plugins/inlineStyles.js';
import * as mergePaths from '../plugins/mergePaths.js';
import * as minifyStyles from '../plugins/minifyStyles.js';
import * as moveElemsAttrsToGroup from '../plugins/moveElemsAttrsToGroup.js';
import * as moveGroupAttrsToElems from '../plugins/moveGroupAttrsToElems.js';
import * as prefixIds from '../plugins/prefixIds.js';
import * as removeAttributesBySelector from '../plugins/removeAttributesBySelector.js';
import * as removeAttrs from '../plugins/removeAttrs.js';
import * as removeComments from '../plugins/removeComments.js';
import * as removeDeprecatedAttrs from '../plugins/removeDeprecatedAttrs.js';
import * as removeDesc from '../plugins/removeDesc.js';
import * as removeDimensions from '../plugins/removeDimensions.js';
import * as removeDoctype from '../plugins/removeDoctype.js';
import * as removeEditorsNSData from '../plugins/removeEditorsNSData.js';
import * as removeElementsByAttr from '../plugins/removeElementsByAttr.js';
import * as removeEmptyAttrs from '../plugins/removeEmptyAttrs.js';
import * as removeEmptyContainers from '../plugins/removeEmptyContainers.js';
import * as removeEmptyText from '../plugins/removeEmptyText.js';
import * as removeHiddenElems from '../plugins/removeHiddenElems.js';
import * as removeMetadata from '../plugins/removeMetadata.js';
import * as removeNonInheritableGroupAttrs from '../plugins/removeNonInheritableGroupAttrs.js';
import * as removeOffCanvasPaths from '../plugins/removeOffCanvasPaths.js';
import * as removeRasterImages from '../plugins/removeRasterImages.js';
import * as removeScriptElement from '../plugins/removeScriptElement.js';
import * as removeStyleElement from '../plugins/removeStyleElement.js';
import * as removeTitle from '../plugins/removeTitle.js';
import * as removeUnknownsAndDefaults from '../plugins/removeUnknownsAndDefaults.js';
import * as removeUnusedNS from '../plugins/removeUnusedNS.js';
import * as removeUselessDefs from '../plugins/removeUselessDefs.js';
import * as removeUselessStrokeAndFill from '../plugins/removeUselessStrokeAndFill.js';
import * as removeViewBox from '../plugins/removeViewBox.js';
import * as removeXlink from '../plugins/removeXlink.js';
import * as removeXMLNS from '../plugins/removeXMLNS.js';
import * as removeXMLProcInst from '../plugins/removeXMLProcInst.js';
import * as reusePaths from '../plugins/reusePaths.js';
import * as sortAttrs from '../plugins/sortAttrs.js';
import * as sortDefsChildren from '../plugins/sortDefsChildren.js';
'use strict';
export const builtin = [
presetDefault,
addAttributesToSVGElement,
addClassesToSVGElement,
cleanupAttrs,
cleanupEnableBackground,
cleanupIds,
cleanupListOfValues,
cleanupNumericValues,
collapseGroups,
convertColors,
convertEllipseToCircle,
convertOneStopGradients,
convertPathData,
convertShapeToPath,
convertStyleToAttrs,
convertTransform,
mergeStyles,
inlineStyles,
mergePaths,
minifyStyles,
moveElemsAttrsToGroup,
moveGroupAttrsToElems,
prefixIds,
removeAttributesBySelector,
removeAttrs,
removeComments,
removeDeprecatedAttrs,
removeDesc,
removeDimensions,
removeDoctype,
removeEditorsNSData,
removeElementsByAttr,
removeEmptyAttrs,
removeEmptyContainers,
removeEmptyText,
removeHiddenElems,
removeMetadata,
removeNonInheritableGroupAttrs,
removeOffCanvasPaths,
removeRasterImages,
removeScriptElement,
removeStyleElement,
removeTitle,
removeUnknownsAndDefaults,
removeUnusedNS,
removeUselessDefs,
removeUselessStrokeAndFill,
removeViewBox,
removeXlink,
removeXMLNS,
removeXMLProcInst,
reusePaths,
sortAttrs,
sortDefsChildren,
exports.builtin = [
require('../plugins/preset-default.js'),
require('../plugins/addAttributesToSVGElement.js'),
require('../plugins/addClassesToSVGElement.js'),
require('../plugins/cleanupAttrs.js'),
require('../plugins/cleanupEnableBackground.js'),
require('../plugins/cleanupIds.js'),
require('../plugins/cleanupListOfValues.js'),
require('../plugins/cleanupNumericValues.js'),
require('../plugins/collapseGroups.js'),
require('../plugins/convertColors.js'),
require('../plugins/convertEllipseToCircle.js'),
require('../plugins/convertOneStopGradients.js'),
require('../plugins/convertPathData.js'),
require('../plugins/convertShapeToPath.js'),
require('../plugins/convertStyleToAttrs.js'),
require('../plugins/convertTransform.js'),
require('../plugins/mergeStyles.js'),
require('../plugins/inlineStyles.js'),
require('../plugins/mergePaths.js'),
require('../plugins/minifyStyles.js'),
require('../plugins/moveElemsAttrsToGroup.js'),
require('../plugins/moveGroupAttrsToElems.js'),
require('../plugins/prefixIds.js'),
require('../plugins/removeAttributesBySelector.js'),
require('../plugins/removeAttrs.js'),
require('../plugins/removeComments.js'),
require('../plugins/removeDesc.js'),
require('../plugins/removeDimensions.js'),
require('../plugins/removeDoctype.js'),
require('../plugins/removeEditorsNSData.js'),
require('../plugins/removeElementsByAttr.js'),
require('../plugins/removeEmptyAttrs.js'),
require('../plugins/removeEmptyContainers.js'),
require('../plugins/removeEmptyText.js'),
require('../plugins/removeHiddenElems.js'),
require('../plugins/removeMetadata.js'),
require('../plugins/removeNonInheritableGroupAttrs.js'),
require('../plugins/removeOffCanvasPaths.js'),
require('../plugins/removeRasterImages.js'),
require('../plugins/removeScriptElement.js'),
require('../plugins/removeStyleElement.js'),
require('../plugins/removeTitle.js'),
require('../plugins/removeUnknownsAndDefaults.js'),
require('../plugins/removeUnusedNS.js'),
require('../plugins/removeUselessDefs.js'),
require('../plugins/removeUselessStrokeAndFill.js'),
require('../plugins/removeViewBox.js'),
require('../plugins/removeXlink.js'),
require('../plugins/removeXMLNS.js'),
require('../plugins/removeXMLProcInst.js'),
require('../plugins/reusePaths.js'),
require('../plugins/sortAttrs.js'),
require('../plugins/sortDefsChildren.js'),
];

@@ -0,17 +1,19 @@

'use strict';
/**
* @typedef {import('./types.js').XastNode} XastNode
* @typedef {import('./types.js').XastInstruction} XastInstruction
* @typedef {import('./types.js').XastDoctype} XastDoctype
* @typedef {import('./types.js').XastComment} XastComment
* @typedef {import('./types.js').XastRoot} XastRoot
* @typedef {import('./types.js').XastElement} XastElement
* @typedef {import('./types.js').XastCdata} XastCdata
* @typedef {import('./types.js').XastText} XastText
* @typedef {import('./types.js').XastParent} XastParent
* @typedef {import('./types.js').XastChild} XastChild
* @typedef {import('./types').XastNode} XastNode
* @typedef {import('./types').XastInstruction} XastInstruction
* @typedef {import('./types').XastDoctype} XastDoctype
* @typedef {import('./types').XastComment} XastComment
* @typedef {import('./types').XastRoot} XastRoot
* @typedef {import('./types').XastElement} XastElement
* @typedef {import('./types').XastCdata} XastCdata
* @typedef {import('./types').XastText} XastText
* @typedef {import('./types').XastParent} XastParent
* @typedef {import('./types').XastChild} XastChild
*/
// @ts-ignore sax will be replaced with something else later
import SAX from '@trysound/sax';
import { textElems } from '../plugins/_collections.js';
const SAX = require('@trysound/sax');
const { textElems } = require('../plugins/_collections');

@@ -90,3 +92,3 @@ class SvgoParserError extends Error {

*/
export const parseSvg = (data, from) => {
const parseSvg = (data, from) => {
const sax = SAX.parser(config.strict, config);

@@ -261,1 +263,2 @@ /**

};
exports.parseSvg = parseSvg;

@@ -1,6 +0,8 @@

import { removeLeadingZero, toFixed } from './svgo/tools.js';
'use strict';
const { removeLeadingZero, toFixed } = require('./svgo/tools');
/**
* @typedef {import('./types.js').PathDataItem} PathDataItem
* @typedef {import('./types.js').PathDataCommand} PathDataCommand
* @typedef {import('./types').PathDataItem} PathDataItem
* @typedef {import('./types').PathDataCommand} PathDataCommand
*/

@@ -138,3 +140,3 @@

*/
export const parsePathData = (string) => {
const parsePathData = (string) => {
/**

@@ -242,2 +244,3 @@ * @type {PathDataItem[]}

};
exports.parsePathData = parsePathData;

@@ -288,3 +291,8 @@ /**

result += roundedStr;
} else if (!Number.isInteger(previous) && !isDigit(roundedStr[0])) {
} else if (
!Number.isInteger(previous) &&
rounded != 0 &&
rounded < 1 &&
rounded > -1
) {
// remove space before decimal with zero whole

@@ -314,7 +322,3 @@ // only when previous number is also decimal

*/
export const stringifyPathData = ({
pathData,
precision,
disableSpaceAfterFlags,
}) => {
const stringifyPathData = ({ pathData, precision, disableSpaceAfterFlags }) => {
if (pathData.length === 1) {

@@ -380,1 +384,2 @@ const { command, args } = pathData[0];

};
exports.stringifyPathData = stringifyPathData;

@@ -1,13 +0,18 @@

import { textElems } from '../plugins/_collections.js';
'use strict';
/**
* @typedef {import('./types.js').XastParent} XastParent
* @typedef {import('./types.js').XastRoot} XastRoot
* @typedef {import('./types.js').XastElement} XastElement
* @typedef {import('./types.js').XastInstruction} XastInstruction
* @typedef {import('./types.js').XastDoctype} XastDoctype
* @typedef {import('./types.js').XastText} XastText
* @typedef {import('./types.js').XastCdata} XastCdata
* @typedef {import('./types.js').XastComment} XastComment
* @typedef {import('./types.js').StringifyOptions} StringifyOptions
* @typedef {import('./types').XastParent} XastParent
* @typedef {import('./types').XastRoot} XastRoot
* @typedef {import('./types').XastElement} XastElement
* @typedef {import('./types').XastInstruction} XastInstruction
* @typedef {import('./types').XastDoctype} XastDoctype
* @typedef {import('./types').XastText} XastText
* @typedef {import('./types').XastCdata} XastCdata
* @typedef {import('./types').XastComment} XastComment
* @typedef {import('./types').StringifyOptions} StringifyOptions
*/
const { textElems } = require('../plugins/_collections');
/**
* @typedef {{

@@ -18,2 +23,5 @@ * indent: string,

* }} State
*/
/**
* @typedef {Required<StringifyOptions>} Options

@@ -73,3 +81,3 @@ */

*/
export const stringifySvg = (data, userOptions = {}) => {
const stringifySvg = (data, userOptions = {}) => {
/**

@@ -111,2 +119,3 @@ * @type {Options}

};
exports.stringifySvg = stringifySvg;

@@ -113,0 +122,0 @@ /**

@@ -1,24 +0,29 @@

import * as csstree from 'css-tree';
import * as csswhat from 'css-what';
import { syntax } from 'csso';
import { visit, matches } from './xast.js';
import {
attrsGroups,
inheritableAttrs,
presentationNonInheritableGroupAttrs,
} from '../plugins/_collections.js';
'use strict';
/**
* @typedef {import('css-tree').Rule} CsstreeRule
* @typedef {import('./types.js').Specificity} Specificity
* @typedef {import('./types.js').Stylesheet} Stylesheet
* @typedef {import('./types.js').StylesheetRule} StylesheetRule
* @typedef {import('./types.js').StylesheetDeclaration} StylesheetDeclaration
* @typedef {import('./types.js').ComputedStyles} ComputedStyles
* @typedef {import('./types.js').XastRoot} XastRoot
* @typedef {import('./types.js').XastElement} XastElement
* @typedef {import('./types.js').XastParent} XastParent
* @typedef {import('./types.js').XastChild} XastChild
* @typedef {import('./types').Specificity} Specificity
* @typedef {import('./types').Stylesheet} Stylesheet
* @typedef {import('./types').StylesheetRule} StylesheetRule
* @typedef {import('./types').StylesheetDeclaration} StylesheetDeclaration
* @typedef {import('./types').ComputedStyles} ComputedStyles
* @typedef {import('./types').XastRoot} XastRoot
* @typedef {import('./types').XastElement} XastElement
* @typedef {import('./types').XastParent} XastParent
* @typedef {import('./types').XastChild} XastChild
*/
const csstree = require('css-tree');
const csswhat = require('css-what');
const {
syntax: { specificity },
} = require('csso');
const { visit, matches } = require('./xast.js');
const {
attrsGroups,
inheritableAttrs,
presentationNonInheritableGroupAttrs,
} = require('../plugins/_collections.js');
// @ts-ignore not defined in @types/csstree
const csstreeWalkSkip = csstree.walk.skip;

@@ -58,3 +63,3 @@

rules.push({
specificity: syntax.specificity(node),
specificity: specificity(node),
dynamic: hasPseudoClasses || dynamic,

@@ -200,3 +205,3 @@ // compute specificity from original node to consider pseudo classes

*/
export const compareSpecificity = (a, b) => {
const compareSpecificity = (a, b) => {
for (let i = 0; i < 4; i += 1) {

@@ -212,2 +217,3 @@ if (a[i] < b[i]) {

};
exports.compareSpecificity = compareSpecificity;

@@ -217,3 +223,3 @@ /**

*/
export const collectStylesheet = (root) => {
const collectStylesheet = (root) => {
/** @type {StylesheetRule[]} */

@@ -254,2 +260,3 @@ const rules = [];

};
exports.collectStylesheet = collectStylesheet;

@@ -261,3 +268,3 @@ /**

*/
export const computeStyle = (stylesheet, node) => {
const computeStyle = (stylesheet, node) => {
const { parents } = stylesheet;

@@ -281,2 +288,3 @@ const computedStyles = computeOwnStyle(stylesheet, node);

};
exports.computeStyle = computeStyle;

@@ -296,3 +304,3 @@ /**

*/
export const includesAttrSelector = (
const includesAttrSelector = (
selector,

@@ -336,1 +344,2 @@ name,

};
exports.includesAttrSelector = includesAttrSelector;

@@ -1,13 +0,22 @@

import os from 'os';
import fs from 'fs';
import { pathToFileURL } from 'url';
import path from 'path';
import { optimize as optimizeAgnostic } from './svgo.js';
'use strict';
const os = require('os');
const fs = require('fs');
const { pathToFileURL } = require('url');
const path = require('path');
const { optimize: optimizeAgnostic } = require('./svgo.js');
const importConfig = async (configFile) => {
// dynamic import expects file url instead of path and may fail
// when windows path is provided
const imported = await import(pathToFileURL(configFile));
const config = imported.default;
let config;
// at the moment dynamic import may randomly fail with segfault
// to workaround this for some users .cjs extension is loaded
// exclusively with require
if (configFile.endsWith('.cjs')) {
config = require(configFile);
} else {
// dynamic import expects file url instead of path and may fail
// when windows path is provided
const { default: imported } = await import(pathToFileURL(configFile));
config = imported;
}
if (config == null || typeof config !== 'object' || Array.isArray(config)) {

@@ -28,3 +37,3 @@ throw Error(`Invalid config file "${configFile}"`);

export const loadConfig = async (configFile, cwd = process.cwd()) => {
const loadConfig = async (configFile, cwd = process.cwd()) => {
if (configFile != null) {

@@ -59,4 +68,5 @@ if (path.isAbsolute(configFile)) {

};
exports.loadConfig = loadConfig;
export const optimize = (input, config) => {
const optimize = (input, config) => {
if (config == null) {

@@ -77,1 +87,2 @@ config = {};

};
exports.optimize = optimize;

@@ -1,11 +0,10 @@

import type { StringifyOptions, DataUri, Plugin } from './types.js';
import type { StringifyOptions, DataUri, Plugin as PluginFn } from './types';
import type {
BuiltinsWithOptionalParams,
BuiltinsWithRequiredParams,
} from '../plugins/plugins-types.js';
} from '../plugins/plugins-types';
type CustomPlugin<T = any> = {
type CustomPlugin = {
name: string;
fn: Plugin<T>;
params?: T;
fn: PluginFn<void>;
};

@@ -57,1 +56,15 @@

export declare function optimize(input: string, config?: Config): Output;
/**
* If you write a tool on top of svgo you might need a way to load svgo config.
*
* You can also specify relative or absolute path and customize current working directory.
*/
export declare function loadConfig(
configFile: string,
cwd?: string,
): Promise<Config>;
export declare function loadConfig(
configFile?: null,
cwd?: string,
): Promise<Config | null>;

@@ -1,7 +0,9 @@

import { parseSvg } from './parser.js';
import { stringifySvg } from './stringifier.js';
import { builtin } from './builtin.js';
import { invokePlugins } from './svgo/plugins.js';
import { encodeSVGDatauri } from './svgo/tools.js';
'use strict';
const { parseSvg } = require('./parser.js');
const { stringifySvg } = require('./stringifier.js');
const { builtin } = require('./builtin.js');
const { invokePlugins } = require('./svgo/plugins.js');
const { encodeSVGDatauri } = require('./svgo/tools.js');
const pluginsMap = {};

@@ -48,3 +50,3 @@ for (const plugin of builtin) {

export const optimize = (input, config) => {
const optimize = (input, config) => {
if (config == null) {

@@ -101,1 +103,2 @@ config = {};

};
exports.optimize = optimize;

@@ -1,12 +0,10 @@

import fs from 'fs';
import path from 'path';
import colors from 'picocolors';
import { fileURLToPath } from 'url';
import { encodeSVGDatauri, decodeSVGDatauri } from './tools.js';
import { loadConfig, optimize } from '../svgo-node.js';
import { builtin } from '../builtin.js';
'use strict';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const pkgPath = path.join(__dirname, '../../package.json');
const PKG = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
const fs = require('fs');
const path = require('path');
const colors = require('picocolors');
const { loadConfig, optimize } = require('../svgo-node.js');
const { builtin } = require('../builtin.js');
const PKG = require('../../package.json');
const { encodeSVGDatauri, decodeSVGDatauri } = require('./tools.js');

@@ -18,13 +16,13 @@ const regSVGFile = /\.svg$/i;

*
* @param {string} filePath
* @param {string} path
*/
export function checkIsDir(filePath) {
function checkIsDir(path) {
try {
return fs.lstatSync(filePath).isDirectory();
return fs.lstatSync(path).isDirectory();
} catch (e) {
return filePath.endsWith(path.sep);
return false;
}
}
export default function makeProgram(program) {
module.exports = function makeProgram(program) {
program

@@ -51,7 +49,4 @@ .name(PKG.name)

)
.option('--config <CONFIG>', 'Custom config file, only .js is supported')
.option(
'--config <CONFIG>',
'Custom config file, only .js, .mjs, and .cjs is supported',
)
.option(
'--datauri <FORMAT>',

@@ -87,3 +82,3 @@ 'Output as Data URI string (base64), URI encoded (enc) or unencoded (unenc)',

.action(action);
}
};

@@ -535,1 +530,3 @@ async function action(args, opts, command) {

}
module.exports.checkIsDir = checkIsDir;
declare let obj: any;
export default obj;
export = obj;

@@ -0,1 +1,3 @@

'use strict';
const isTag = (node) => {

@@ -118,2 +120,2 @@ return node.type === 'element';

export default svgoCssSelectAdapter;
module.exports = svgoCssSelectAdapter;

@@ -1,3 +0,5 @@

import { visit } from '../xast.js';
'use strict';
const { visit } = require('../xast.js');
/**

@@ -13,9 +15,3 @@ * Plugins engine.

*/
export const invokePlugins = (
ast,
info,
plugins,
overrides,
globalOverrides,
) => {
const invokePlugins = (ast, info, plugins, overrides, globalOverrides) => {
for (const plugin of plugins) {

@@ -34,4 +30,5 @@ const override = overrides?.[plugin.name];

};
exports.invokePlugins = invokePlugins;
export const createPreset = ({ name, plugins }) => {
const createPreset = ({ name, plugins }) => {
return {

@@ -66,1 +63,2 @@ name,

};
exports.createPreset = createPreset;

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

'use strict';
/**
* @typedef {import('../types.js').DataUri} DataUri
* @typedef {import('../types.js').PathDataCommand} PathDataCommand
* @typedef {import('../../lib/types.js').XastElement} XastElement
* @typedef {import('../../lib/types').XastElement} XastElement
* @typedef {import('../types').PathDataCommand} PathDataCommand
* @typedef {import('../types').DataUri} DataUri
*/
import { attrsGroups, referencesProps } from '../../plugins/_collections.js';
const { attrsGroups, referencesProps } = require('../../plugins/_collections');

@@ -18,3 +20,3 @@ const regReferencesUrl = /\burl\((["'])?#(.+?)\1\)/g;

*/
export const encodeSVGDatauri = (str, type) => {
exports.encodeSVGDatauri = (str, type) => {
var prefix = 'data:image/svg+xml';

@@ -40,3 +42,3 @@ if (!type || type === 'base64') {

*/
export const decodeSVGDatauri = (str) => {
exports.decodeSVGDatauri = (str) => {
var regexp = /data:image\/svg\+xml(;charset=[^;,]*)?(;base64)?,(.*)/;

@@ -79,3 +81,3 @@ var match = regexp.exec(str);

*/
export const cleanupOutData = (data, params, command) => {
exports.cleanupOutData = (data, params, command) => {
let str = '';

@@ -134,3 +136,3 @@ let delimiter;

*/
export const removeLeadingZero = (value) => {
const removeLeadingZero = (value) => {
const strValue = value.toString();

@@ -148,2 +150,3 @@

};
exports.removeLeadingZero = removeLeadingZero;

@@ -157,3 +160,3 @@ /**

*/
export const hasScripts = (node) => {
const hasScripts = (node) => {
if (node.name === 'script' && node.children.length !== 0) {

@@ -186,2 +189,3 @@ return true;

};
exports.hasScripts = hasScripts;

@@ -198,5 +202,6 @@ /**

*/
export const includesUrlReference = (body) => {
const includesUrlReference = (body) => {
return new RegExp(regReferencesUrl).test(body);
};
exports.includesUrlReference = includesUrlReference;

@@ -208,3 +213,3 @@ /**

*/
export const findReferences = (attribute, value) => {
const findReferences = (attribute, value) => {
const results = [];

@@ -235,2 +240,3 @@

};
exports.findReferences = findReferences;

@@ -245,5 +251,6 @@ /**

*/
export const toFixed = (num, precision) => {
const toFixed = (num, precision) => {
const pow = 10 ** precision;
return Math.round(num * pow) / pow;
};
exports.toFixed = toFixed;

@@ -112,3 +112,3 @@ export type XastDoctype = {

info: PluginInfo,
) => Visitor | null | void;
) => null | Visitor;

@@ -115,0 +115,0 @@ export type Specificity = [number, number, number];

@@ -1,11 +0,13 @@

import { selectAll, selectOne, is } from 'css-select';
import xastAdaptor from './svgo/css-select-adapter.js';
'use strict';
/**
* @typedef {import('./types.js').XastNode} XastNode
* @typedef {import('./types.js').XastChild} XastChild
* @typedef {import('./types.js').XastParent} XastParent
* @typedef {import('./types.js').Visitor} Visitor
* @typedef {import('./types').XastNode} XastNode
* @typedef {import('./types').XastChild} XastChild
* @typedef {import('./types').XastParent} XastParent
* @typedef {import('./types').Visitor} Visitor
*/
const { selectAll, selectOne, is } = require('css-select');
const xastAdaptor = require('./svgo/css-select-adapter.js');
const cssSelectOptions = {

@@ -19,5 +21,6 @@ xmlMode: true,

*/
export const querySelectorAll = (node, selector) => {
const querySelectorAll = (node, selector) => {
return selectAll(selector, node, cssSelectOptions);
};
exports.querySelectorAll = querySelectorAll;

@@ -27,5 +30,6 @@ /**

*/
export const querySelector = (node, selector) => {
const querySelector = (node, selector) => {
return selectOne(selector, node, cssSelectOptions);
};
exports.querySelector = querySelector;

@@ -35,7 +39,9 @@ /**

*/
export const matches = (node, selector) => {
const matches = (node, selector) => {
return is(node, selector, cssSelectOptions);
};
exports.matches = matches;
export const visitSkip = Symbol();
const visitSkip = Symbol();
exports.visitSkip = visitSkip;

@@ -45,3 +51,3 @@ /**

*/
export const visit = (node, visitor, parentNode) => {
const visit = (node, visitor, parentNode) => {
const callbacks = visitor[node.type];

@@ -75,2 +81,3 @@ if (callbacks && callbacks.enter) {

};
exports.visit = visit;

@@ -81,5 +88,6 @@ /**

*/
export const detachNodeFromParent = (node, parentNode) => {
const detachNodeFromParent = (node, parentNode) => {
// avoid splice to not break for loops
parentNode.children = parentNode.children.filter((child) => child !== node);
};
exports.detachNodeFromParent = detachNodeFromParent;
{
"packageManager": "yarn@2.4.3",
"name": "svgo",
"version": "3.3.1",
"version": "3.3.2",
"description": "Nodejs-based tool for optimizing SVG vector graphics files",
"license": "MIT",
"type": "module",
"keywords": [

@@ -54,25 +53,4 @@ "svgo",

"main": "./lib/svgo-node.js",
"bin": "./bin/svgo.js",
"types": "./lib/svgo-node.d.ts",
"exports": {
".": {
"import": "./lib/svgo-node.js",
"require": "./dist/svgo-node.cjs",
"types": "./lib/svgo-node.d.ts"
},
"./browser": {
"import": "./dist/svgo.browser.js",
"types": "./lib/svgo.d.ts"
}
},
"typesVersions": {
"*": {
".": [
"./lib/svgo-node.d.ts"
],
"browser": [
"./lib/svgo.d.ts"
]
}
},
"bin": "./bin/svgo",
"types": "./lib/svgo.d.ts",
"files": [

@@ -93,7 +71,6 @@ "bin",

"typecheck": "tsc",
"generate-bundles": "rollup -c",
"test-bundles": "yarn generate-bundles && node ./test/svgo.cjs && node ./test/browser.js",
"test-browser": "rollup -c && node ./test/browser.js",
"test-regression": "node ./test/regression-extract.js && NO_DIFF=1 node ./test/regression.js",
"prepublishOnly": "rm -rf dist && yarn generate-bundles",
"qa": "yarn lint && yarn typecheck && yarn test && yarn test-bundles && yarn test-regression"
"prepublishOnly": "rm -rf dist && rollup -c",
"qa": "yarn lint && yarn typecheck && yarn test && yarn test-browser && yarn test-regression"
},

@@ -115,6 +92,5 @@ "jest": {

"devDependencies": {
"@rollup/plugin-commonjs": "^25.0.7",
"@rollup/plugin-node-resolve": "^15.2.3",
"@rollup/plugin-terser": "^0.4.4",
"@types/css-tree": "^2.3.5",
"@rollup/plugin-commonjs": "^22.0.2",
"@rollup/plugin-node-resolve": "^14.1.0",
"@types/css-tree": "^2.3.4",
"@types/csso": "^5.0.4",

@@ -130,3 +106,4 @@ "@types/jest": "^29.5.5",

"prettier": "^3.1.1",
"rollup": "^4.9.2",
"rollup": "^2.79.1",
"rollup-plugin-terser": "^7.0.2",
"tar-stream": "^3.1.6",

@@ -133,0 +110,0 @@ "typescript": "^5.3.3"

@@ -0,1 +1,3 @@

'use strict';
// https://www.w3.org/TR/SVG11/intro.html#Definitions

@@ -6,3 +8,3 @@

*/
export const elemsGroups = {
exports.elemsGroups = {
animation: new Set([

@@ -59,3 +61,2 @@ 'animate',

textContent: new Set([
'a',
'altGlyph',

@@ -111,5 +112,9 @@ 'altGlyphDef',

*/
export const textElems = new Set([...elemsGroups.textContent, 'pre', 'title']);
exports.textElems = new Set([
...exports.elemsGroups.textContent,
'pre',
'title',
]);
export const pathElems = new Set(['glyph', 'missing-glyph', 'path']);
exports.pathElems = new Set(['glyph', 'missing-glyph', 'path']);

@@ -120,3 +125,3 @@ /**

*/
export const attrsGroups = {
exports.attrsGroups = {
animationAddition: new Set(['additive', 'accumulate']),

@@ -319,3 +324,3 @@ animationAttributeTarget: new Set(['attributeType', 'attributeName']),

*/
export const attrsGroupsDefaults = {
exports.attrsGroupsDefaults = {
core: { 'xml:space': 'default' },

@@ -385,22 +390,2 @@ presentation: {

/**
* @type {Record<string, { safe?: Set<string>, unsafe?: Set<string> }>}
* @see https://www.w3.org/TR/SVG11/intro.html#Definitions
*/
export const attrsGroupsDeprecated = {
animationAttributeTarget: { unsafe: new Set(['attributeType']) },
conditionalProcessing: { unsafe: new Set(['requiredFeatures']) },
core: { unsafe: new Set(['xml:base', 'xml:lang', 'xml:space']) },
presentation: {
unsafe: new Set([
'clip',
'color-profile',
'enable-background',
'glyph-orientation-horizontal',
'glyph-orientation-vertical',
'kerning',
]),
},
};
/**
* @type {Record<string, {

@@ -410,6 +395,2 @@ * attrsGroups: Set<string>,

* defaults?: Record<string, string>,
* deprecated?: {
* safe?: Set<string>,
* unsafe?: Set<string>,
* },
* contentGroups?: Set<string>,

@@ -420,3 +401,3 @@ * content?: Set<string>,

*/
export const elems = {
exports.elems = {
a: {

@@ -610,3 +591,2 @@ attrsGroups: new Set([

},
deprecated: { unsafe: new Set(['name']) },
contentGroups: new Set(['descriptive']),

@@ -996,3 +976,2 @@ },

},
deprecated: { unsafe: new Set(['filterRes']) },
contentGroups: new Set(['descriptive', 'filterPrimitive']),

@@ -1018,11 +997,2 @@ content: new Set(['animate', 'set']),

},
deprecated: {
unsafe: new Set([
'horiz-origin-x',
'horiz-origin-y',
'vert-adv-y',
'vert-origin-x',
'vert-origin-y',
]),
},
contentGroups: new Set(['descriptive']),

@@ -1078,27 +1048,2 @@ content: new Set(['font-face', 'glyph', 'hkern', 'missing-glyph', 'vkern']),

},
deprecated: {
unsafe: new Set([
'accent-height',
'alphabetic',
'ascent',
'bbox',
'cap-height',
'descent',
'hanging',
'ideographic',
'mathematical',
'panose-1',
'slope',
'stemh',
'stemv',
'unicode-range',
'units-per-em',
'v-alphabetic',
'v-hanging',
'v-ideographic',
'v-mathematical',
'widths',
'x-height',
]),
},
contentGroups: new Set(['descriptive']),

@@ -1114,3 +1059,2 @@ content: new Set([

attrs: new Set(['string']),
deprecated: { unsafe: new Set(['string']) },
},

@@ -1120,3 +1064,2 @@ 'font-face-name': {

attrs: new Set(['name']),
deprecated: { unsafe: new Set(['name']) },
},

@@ -1214,14 +1157,2 @@ 'font-face-src': {

},
deprecated: {
unsafe: new Set([
'arabic-form',
'glyph-name',
'horiz-adv-x',
'orientation',
'unicode',
'vert-adv-y',
'vert-origin-x',
'vert-origin-y',
]),
},
contentGroups: new Set([

@@ -1266,10 +1197,2 @@ 'animation',

]),
deprecated: {
unsafe: new Set([
'horiz-adv-x',
'vert-adv-y',
'vert-origin-x',
'vert-origin-y',
]),
},
contentGroups: new Set([

@@ -1338,3 +1261,2 @@ 'animation',

attrs: new Set(['u1', 'g1', 'u2', 'g2', 'k']),
deprecated: { unsafe: new Set(['g1', 'g2', 'k', 'u1', 'u2']) },
},

@@ -1534,10 +1456,2 @@ image: {

]),
deprecated: {
unsafe: new Set([
'horiz-adv-x',
'vert-adv-y',
'vert-origin-x',
'vert-origin-y',
]),
},
contentGroups: new Set([

@@ -1823,11 +1737,2 @@ 'animation',

},
deprecated: {
safe: new Set(['version']),
unsafe: new Set([
'baseProfile',
'contentScriptType',
'contentStyleType',
'zoomAndPan',
]),
},
contentGroups: new Set([

@@ -2079,3 +1984,2 @@ 'animation',

]),
deprecated: { unsafe: new Set(['viewTarget', 'zoomAndPan']) },
contentGroups: new Set(['descriptive']),

@@ -2086,3 +1990,2 @@ },

attrs: new Set(['u1', 'g1', 'u2', 'g2', 'k']),
deprecated: { unsafe: new Set(['g1', 'g2', 'k', 'u1', 'u2']) },
},

@@ -2092,3 +1995,3 @@ };

// https://wiki.inkscape.org/wiki/index.php/Inkscape-specific_XML_attributes
export const editorNamespaces = new Set([
exports.editorNamespaces = new Set([
'http://creativecommons.org/ns#',

@@ -2121,3 +2024,3 @@ 'http://inkscape.sourceforge.net/DTD/sodipodi-0.dtd',

*/
export const referencesProps = new Set([
exports.referencesProps = new Set([
'clip-path',

@@ -2138,3 +2041,3 @@ 'color-profile',

*/
export const inheritableAttrs = new Set([
exports.inheritableAttrs = new Set([
'clip-rule',

@@ -2187,3 +2090,3 @@ 'color-interpolation-filters',

export const presentationNonInheritableGroupAttrs = new Set([
exports.presentationNonInheritableGroupAttrs = new Set([
'clip-path',

@@ -2204,3 +2107,3 @@ 'display',

*/
export const colorsNames = {
exports.colorsNames = {
aliceblue: '#f0f8ff',

@@ -2359,3 +2262,3 @@ antiquewhite: '#faebd7',

*/
export const colorsShortNames = {
exports.colorsShortNames = {
'#f0ffff': 'azure',

@@ -2398,3 +2301,3 @@ '#f5f5dc': 'beige',

*/
export const colorsProps = new Set([
exports.colorsProps = new Set([
'color',

@@ -2409,3 +2312,3 @@ 'fill',

/** @see https://developer.mozilla.org/docs/Web/CSS/Pseudo-classes */
export const pseudoClasses = {
exports.pseudoClasses = {
displayState: new Set(['fullscreen', 'modal', 'picture-in-picture']),

@@ -2412,0 +2315,0 @@ input: new Set([

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

import { parsePathData, stringifyPathData } from '../lib/path.js';
'use strict';
/**
* @typedef {import('../lib/types.js').XastElement} XastElement
* @typedef {import('../lib/types.js').PathDataItem} PathDataItem
* @typedef {import('../lib/types').XastElement} XastElement
* @typedef {import('../lib/types').PathDataItem} PathDataItem
*/
const { parsePathData, stringifyPathData } = require('../lib/path.js');
/**

@@ -18,3 +20,3 @@ * @type {[number, number]}

*/
export const path2js = (path) => {
const path2js = (path) => {
// @ts-ignore legacy

@@ -38,2 +40,3 @@ if (path.pathJS) return path.pathJS;

};
exports.path2js = path2js;

@@ -183,3 +186,3 @@ /**

*/
export const js2path = function (path, data, params) {
exports.js2path = function (path, data, params) {
// @ts-ignore legacy

@@ -229,3 +232,3 @@ path.pathJS = data;

*/
export const intersects = function (path1, path2) {
exports.intersects = function (path1, path2) {
// Collect points of every subpath.

@@ -232,0 +235,0 @@ const points1 = gatherPoints(convertRelativeToAbsolute(path1));

@@ -1,3 +0,5 @@

import { cleanupOutData, toFixed } from '../lib/svgo/tools.js';
'use strict';
const { toFixed } = require('../lib/svgo/tools');
/**

@@ -7,3 +9,2 @@ * @typedef {{ name: string, data: number[] }} TransformItem

* convertToShorts: boolean,
* degPrecision?: number,
* floatPrecision: number,

@@ -41,3 +42,3 @@ * transformPrecision: number,

*/
export const transform2js = (transformString) => {
exports.transform2js = (transformString) => {
/** @type {TransformItem[]} */

@@ -73,2 +74,3 @@ const transforms = [];

};
/**

@@ -80,3 +82,3 @@ * Multiply transforms into one.

*/
export const transformsMultiply = (transforms) => {
exports.transformsMultiply = (transforms) => {
const matrixData = transforms.map((transform) => {

@@ -171,321 +173,111 @@ if (transform.name === 'matrix') {

/**
* @param {TransformItem} matrix
* @returns {TransformItem[][]}
* Decompose matrix into simple transforms.
*
* @param {TransformItem} transform
* @param {TransformParams} params
* @returns {TransformItem[]}
* @see https://frederic-wang.fr/decomposition-of-2d-transform-matrices.html
*/
const getDecompositions = (matrix) => {
const decompositions = [];
const qrab = decomposeQRAB(matrix);
const qrcd = decomposeQRCD(matrix);
exports.matrixToTransform = (transform, params) => {
const floatPrecision = params.floatPrecision;
const data = transform.data;
const transforms = [];
if (qrab) {
decompositions.push(qrab);
}
if (qrcd) {
decompositions.push(qrcd);
}
return decompositions;
};
/**
* @param {TransformItem} matrix
* @returns {TransformItem[]|undefined}
* @see {@link https://frederic-wang.fr/decomposition-of-2d-transform-matrices.html} Where applicable, variables are named in accordance with this document.
*/
const decomposeQRAB = (matrix) => {
const data = matrix.data;
const [a, b, c, d, e, f] = data;
const delta = a * d - b * c;
if (delta === 0) {
return;
}
const r = Math.hypot(a, b);
if (r === 0) {
return;
}
const decomposition = [];
const cosOfRotationAngle = a / r;
// [..., ..., ..., ..., tx, ty] → translate(tx, ty)
if (e || f) {
decomposition.push({
if (data[4] || data[5]) {
transforms.push({
name: 'translate',
data: [e, f],
data: data.slice(4, data[5] ? 6 : 5),
});
}
if (cosOfRotationAngle !== 1) {
const rotationAngleRads = Math.acos(cosOfRotationAngle);
decomposition.push({
name: 'rotate',
data: [mth.deg(b < 0 ? -rotationAngleRads : rotationAngleRads), 0, 0],
});
}
let sx = toFixed(Math.hypot(data[0], data[1]), params.transformPrecision);
let sy = toFixed(
(data[0] * data[3] - data[1] * data[2]) / sx,
params.transformPrecision,
);
const colsSum = data[0] * data[2] + data[1] * data[3];
const rowsSum = data[0] * data[1] + data[2] * data[3];
const scaleBefore = rowsSum !== 0 || sx === sy;
const sx = r;
const sy = delta / sx;
if (sx !== 1 || sy !== 1) {
decomposition.push({ name: 'scale', data: [sx, sy] });
}
const ac_plus_bd = a * c + b * d;
if (ac_plus_bd) {
decomposition.push({
// [sx, 0, tan(a)·sy, sy, 0, 0] → skewX(a)·scale(sx, sy)
if (!data[1] && data[2]) {
transforms.push({
name: 'skewX',
data: [mth.deg(Math.atan(ac_plus_bd / (a * a + b * b)))],
data: [mth.atan(data[2] / sy, floatPrecision)],
});
}
return decomposition;
};
/**
* @param {TransformItem} matrix
* @returns {TransformItem[]|undefined}
* @see {@link https://frederic-wang.fr/decomposition-of-2d-transform-matrices.html} Where applicable, variables are named in accordance with this document.
*/
const decomposeQRCD = (matrix) => {
const data = matrix.data;
const [a, b, c, d, e, f] = data;
const delta = a * d - b * c;
if (delta === 0) {
return;
}
const s = Math.hypot(c, d);
if (s === 0) {
return;
}
const decomposition = [];
if (e || f) {
decomposition.push({
name: 'translate',
data: [e, f],
});
}
const rotationAngleRads = Math.PI / 2 - (d < 0 ? -1 : 1) * Math.acos(-c / s);
decomposition.push({
name: 'rotate',
data: [mth.deg(rotationAngleRads), 0, 0],
});
const sx = delta / s;
const sy = s;
if (sx !== 1 || sy !== 1) {
decomposition.push({ name: 'scale', data: [sx, sy] });
}
const ac_plus_bd = a * c + b * d;
if (ac_plus_bd) {
decomposition.push({
// [sx, sx·tan(a), 0, sy, 0, 0] → skewY(a)·scale(sx, sy)
} else if (data[1] && !data[2]) {
transforms.push({
name: 'skewY',
data: [mth.deg(Math.atan(ac_plus_bd / (c * c + d * d)))],
data: [mth.atan(data[1] / data[0], floatPrecision)],
});
}
sx = data[0];
sy = data[3];
return decomposition;
};
// [sx·cos(a), sx·sin(a), sy·-sin(a), sy·cos(a), x, y] → rotate(a[, cx, cy])·(scale or skewX) or
// [sx·cos(a), sy·sin(a), sx·-sin(a), sy·cos(a), x, y] → scale(sx, sy)·rotate(a[, cx, cy]) (if !scaleBefore)
} else if (!colsSum || (sx === 1 && sy === 1) || !scaleBefore) {
if (!scaleBefore) {
sx = Math.hypot(data[0], data[2]);
sy = Math.hypot(data[1], data[3]);
/**
* Convert translate(tx,ty)rotate(a) to rotate(a,cx,cy).
* @param {number} tx
* @param {number} ty
* @param {number} a
* @returns {TransformItem}
*/
const mergeTranslateAndRotate = (tx, ty, a) => {
// From https://www.w3.org/TR/SVG11/coords.html#TransformAttribute:
// We have translate(tx,ty) rotate(a). This is equivalent to [cos(a) sin(a) -sin(a) cos(a) tx ty].
//
// rotate(a,cx,cy) is equivalent to translate(cx, cy) rotate(a) translate(-cx, -cy).
// Multiplying the right side gives the matrix
// [cos(a) sin(a) -sin(a) cos(a)
// -cx * cos(a) + cy * sin(a) + cx
// -cx * sin(a) - cy * cos(a) + cy
// ]
//
// We need cx and cy such that
// tx = -cx * cos(a) + cy * sin(a) + cx
// ty = -cx * sin(a) - cy * cos(a) + cy
//
// Solving these for cx and cy gives
// cy = (d * ty + e * tx)/(d^2 + e^2)
// cx = (tx - e * cy) / d
// where d = 1 - cos(a) and e = sin(a)
if (toFixed(data[0], params.transformPrecision) < 0) {
sx = -sx;
}
const rotationAngleRads = mth.rad(a);
const d = 1 - Math.cos(rotationAngleRads);
const e = Math.sin(rotationAngleRads);
const cy = (d * ty + e * tx) / (d * d + e * e);
const cx = (tx - e * cy) / d;
return { name: 'rotate', data: [a, cx, cy] };
};
if (
data[3] < 0 ||
(Math.sign(data[1]) === Math.sign(data[2]) &&
toFixed(data[3], params.transformPrecision) === 0)
) {
sy = -sy;
}
/**
* @param {TransformItem} t
* @returns {Boolean}
*/
const isIdentityTransform = (t) => {
switch (t.name) {
case 'rotate':
case 'skewX':
case 'skewY':
return t.data[0] === 0;
case 'scale':
return t.data[0] === 1 && t.data[1] === 1;
case 'translate':
return t.data[0] === 0 && t.data[1] === 0;
}
return false;
};
transforms.push({ name: 'scale', data: [sx, sy] });
}
const angle = Math.min(Math.max(-1, data[0] / sx), 1);
const rotate = [
mth.acos(angle, floatPrecision) *
((scaleBefore ? 1 : sy) * data[1] < 0 ? -1 : 1),
];
/**
* Optimize matrix of simple transforms.
* @param {TransformItem[]} roundedTransforms
* @param {TransformItem[]} rawTransforms
* @returns {TransformItem[]}
*/
const optimize = (roundedTransforms, rawTransforms) => {
const optimizedTransforms = [];
if (rotate[0]) {
transforms.push({ name: 'rotate', data: rotate });
}
for (let index = 0; index < roundedTransforms.length; index++) {
const roundedTransform = roundedTransforms[index];
if (rowsSum && colsSum)
transforms.push({
name: 'skewX',
data: [mth.atan(colsSum / (sx * sx), floatPrecision)],
});
// Don't include any identity transforms.
if (isIdentityTransform(roundedTransform)) {
continue;
// rotate(a, cx, cy) can consume translate() within optional arguments cx, cy (rotation point)
if (rotate[0] && (data[4] || data[5])) {
transforms.shift();
const oneOverCos = 1 - data[0] / sx;
const sin = data[1] / (scaleBefore ? sx : sy);
const x = data[4] * (scaleBefore ? 1 : sy);
const y = data[5] * (scaleBefore ? 1 : sx);
const denom = (oneOverCos ** 2 + sin ** 2) * (scaleBefore ? 1 : sx * sy);
rotate.push(
(oneOverCos * x - sin * y) / denom,
(oneOverCos * y + sin * x) / denom,
);
}
const data = roundedTransform.data;
switch (roundedTransform.name) {
case 'rotate':
switch (data[0]) {
case 180:
case -180:
{
// If the next element is a scale, invert it, and don't add the rotate to the optimized array.
const next = roundedTransforms[index + 1];
if (next && next.name === 'scale') {
optimizedTransforms.push(
createScaleTransform(next.data.map((v) => -v)),
);
index++;
} else {
// Otherwise replace the rotate with a scale(-1).
optimizedTransforms.push({
name: 'scale',
data: [-1],
});
}
}
continue;
}
optimizedTransforms.push({
name: 'rotate',
data: data.slice(0, data[1] || data[2] ? 3 : 1),
});
break;
case 'scale':
optimizedTransforms.push(createScaleTransform(data));
break;
case 'skewX':
case 'skewY':
optimizedTransforms.push({
name: roundedTransform.name,
data: [data[0]],
});
break;
case 'translate':
{
// If the next item is a rotate(a,0,0), merge the translate and rotate.
// If the rotation angle is +/-180, assume it will be optimized out, and don't do the merge.
const next = roundedTransforms[index + 1];
if (
next &&
next.name === 'rotate' &&
next.data[0] !== 180 &&
next.data[0] !== -180 &&
next.data[0] !== 0 &&
next.data[1] === 0 &&
next.data[2] === 0
) {
// Use the un-rounded data to do the merge.
const data = rawTransforms[index].data;
optimizedTransforms.push(
mergeTranslateAndRotate(
data[0],
data[1],
rawTransforms[index + 1].data[0],
),
);
// Skip over the rotate.
index++;
continue;
}
}
optimizedTransforms.push({
name: 'translate',
data: data.slice(0, data[1] ? 2 : 1),
});
break;
}
// Too many transformations, return original matrix if it isn't just a scale/translate
} else if (data[1] || data[2]) {
return [transform];
}
// If everything was optimized out, reture identity transform scale(1).
return optimizedTransforms.length
? optimizedTransforms
: [{ name: 'scale', data: [1] }];
};
/**
* @param {number[]} data
* @returns {TransformItem}
*/
const createScaleTransform = (data) => {
const scaleData = data.slice(0, data[0] === data[1] ? 1 : 2);
return {
name: 'scale',
data: scaleData,
};
};
/**
* Decompose matrix into simple transforms and optimize.
* @param {TransformItem} origMatrix
* @param {TransformParams} params
* @returns {TransformItem[]}
*/
export const matrixToTransform = (origMatrix, params) => {
const decomposed = getDecompositions(origMatrix);
let shortest;
let shortestLen = Number.MAX_VALUE;
for (const decomposition of decomposed) {
// Make a copy of the decomposed matrix, and round all data. We need to keep the original decomposition,
// at full precision, to perform some optimizations.
const roundedTransforms = decomposition.map((transformItem) => {
const transformCopy = {
name: transformItem.name,
data: [...transformItem.data],
};
return roundTransform(transformCopy, params);
if ((scaleBefore && (sx != 1 || sy != 1)) || !transforms.length) {
transforms.push({
name: 'scale',
data: sx == sy ? [sx] : [sx, sy],
});
const optimized = optimize(roundedTransforms, decomposition);
const len = js2transform(optimized, params).length;
if (len < shortestLen) {
shortest = optimized;
shortestLen = len;
}
}
return shortest ?? [origMatrix];
return transforms;
};

@@ -512,3 +304,3 @@

0,
transform.data[1] ?? transform.data[0],
transform.data[1] || transform.data[0],
0,

@@ -554,3 +346,3 @@ 0,

*/
export const transformArc = (cursor, arc, transform) => {
exports.transformArc = (cursor, arc, transform) => {
const x = arc[5] - cursor[0];

@@ -626,124 +418,1 @@ const y = arc[6] - cursor[1];

};
/**
* @type {(transform: TransformItem, params: TransformParams) => TransformItem}
*/
export const roundTransform = (transform, params) => {
switch (transform.name) {
case 'translate':
transform.data = floatRound(transform.data, params);
break;
case 'rotate':
transform.data = [
...degRound(transform.data.slice(0, 1), params),
...floatRound(transform.data.slice(1), params),
];
break;
case 'skewX':
case 'skewY':
transform.data = degRound(transform.data, params);
break;
case 'scale':
transform.data = transformRound(transform.data, params);
break;
case 'matrix':
transform.data = [
...transformRound(transform.data.slice(0, 4), params),
...floatRound(transform.data.slice(4), params),
];
break;
}
return transform;
};
/**
* @type {(data: number[], params: TransformParams) => number[]}
*/
const degRound = (data, params) => {
if (
params.degPrecision != null &&
params.degPrecision >= 1 &&
params.floatPrecision < 20
) {
return smartRound(params.degPrecision, data);
} else {
return round(data);
}
};
/**
* @type {(data: number[], params: TransformParams) => number[]}
*/
const floatRound = (data, params) => {
if (params.floatPrecision >= 1 && params.floatPrecision < 20) {
return smartRound(params.floatPrecision, data);
} else {
return round(data);
}
};
/**
* @type {(data: number[], params: TransformParams) => number[]}
*/
const transformRound = (data, params) => {
if (params.transformPrecision >= 1 && params.floatPrecision < 20) {
return smartRound(params.transformPrecision, data);
} else {
return round(data);
}
};
/**
* Rounds numbers in array.
*
* @type {(data: number[]) => number[]}
*/
const round = (data) => {
return data.map(Math.round);
};
/**
* Decrease accuracy of floating-point numbers
* in transforms keeping a specified number of decimals.
* Smart rounds values like 2.349 to 2.35.
*
* @param {number} precision
* @param {number[]} data
* @returns {number[]}
*/
const smartRound = (precision, data) => {
for (
var i = data.length,
tolerance = +Math.pow(0.1, precision).toFixed(precision);
i--;
) {
if (toFixed(data[i], precision) !== data[i]) {
var rounded = +data[i].toFixed(precision - 1);
data[i] =
+Math.abs(rounded - data[i]).toFixed(precision + 1) >= tolerance
? +data[i].toFixed(precision)
: rounded;
}
}
return data;
};
/**
* Convert transforms JS representation to string.
*
* @param {TransformItem[]} transformJS
* @param {TransformParams} params
* @returns {string}
*/
export const js2transform = (transformJS, params) => {
const transformString = transformJS
.map((transform) => {
roundTransform(transform, params);
return `${transform.name}(${cleanupOutData(transform.data, params)})`;
})
.join('');
return transformString;
};

@@ -1,4 +0,6 @@

export const name = 'addAttributesToSVGElement';
export const description = 'adds attributes to an outer <svg> element';
'use strict';
exports.name = 'addAttributesToSVGElement';
exports.description = 'adds attributes to an outer <svg> element';
var ENOCLS = `Error in plugin "addAttributesToSVGElement": absent parameters.

@@ -48,5 +50,5 @@ It should have a list of "attributes" or one "attribute".

*
* @type {import('./plugins-types.js').Plugin<'addAttributesToSVGElement'>}
* @type {import('./plugins-types').Plugin<'addAttributesToSVGElement'>}
*/
export const fn = (root, params) => {
exports.fn = (root, params) => {
if (!Array.isArray(params.attributes) && !params.attribute) {

@@ -53,0 +55,0 @@ console.error(ENOCLS);

@@ -1,4 +0,6 @@

export const name = 'addClassesToSVGElement';
export const description = 'adds classnames to an outer <svg> element';
'use strict';
exports.name = 'addClassesToSVGElement';
exports.description = 'adds classnames to an outer <svg> element';
var ENOCLS = `Error in plugin "addClassesToSVGElement": absent parameters.

@@ -50,7 +52,7 @@ It should have a list of classes in "classNames" or one "className".

*
* @type {import('./plugins-types.js').Plugin<'addClassesToSVGElement'>}
* @type {import('./plugins-types').Plugin<'addClassesToSVGElement'>}
*/
export const fn = (root, params, info) => {
exports.fn = (root, params) => {
if (
!(Array.isArray(params.classNames) && params.classNames.length !== 0) &&
!(Array.isArray(params.classNames) && params.classNames.some(String)) &&
!params.className

@@ -73,7 +75,3 @@ ) {

if (className != null) {
const classToAdd =
typeof className === 'string'
? className
: className(node, info);
classList.add(classToAdd);
classList.add(className);
}

@@ -80,0 +78,0 @@ }

@@ -0,17 +1,21 @@

'use strict';
/**
* @typedef {import('../lib/types.js').PathDataItem} PathDataItem
* @typedef {import('../lib/types.js').XastElement} XastElement
* @typedef {import('../lib/types').PathDataItem} PathDataItem
* @typedef {import('../lib/types').XastElement} XastElement
*/
import { path2js } from './_path.js';
import {
const { collectStylesheet, computeStyle } = require('../lib/style.js');
const {
transformsMultiply,
transform2js,
transformArc,
} from './_transforms.js';
import { referencesProps, attrsGroupsDefaults } from './_collections.js';
import { collectStylesheet, computeStyle } from '../lib/style.js';
} = require('./_transforms.js');
const { path2js } = require('./_path.js');
const {
removeLeadingZero,
includesUrlReference,
} = require('../lib/svgo/tools.js');
const { referencesProps, attrsGroupsDefaults } = require('./_collections.js');
import { removeLeadingZero, includesUrlReference } from '../lib/svgo/tools.js';
/**

@@ -27,3 +31,3 @@ * @typedef {PathDataItem[]} PathData

*
* @type {import('../lib/types.js').Plugin<{
* @type {import('../lib/types').Plugin<{
* transformPrecision: number,

@@ -33,3 +37,3 @@ * applyTransformsStroked: boolean,

*/
export const applyTransforms = (root, params) => {
const applyTransforms = (root, params) => {
const stylesheet = collectStylesheet(root);

@@ -98,5 +102,5 @@ return {

const scale = Number(
Math.hypot(matrix.data[0], matrix.data[1]).toFixed(
transformPrecision,
),
Math.sqrt(
matrix.data[0] * matrix.data[0] + matrix.data[1] * matrix.data[1],
).toFixed(transformPrecision),
);

@@ -162,2 +166,3 @@

};
exports.applyTransforms = applyTransforms;

@@ -219,3 +224,3 @@ /**

// horizontal lineto (x)
// convert to lineto to handle two-dimensional transforms
// convert to lineto to handle two-dimentional transforms
if (command === 'H') {

@@ -231,3 +236,3 @@ command = 'L';

// vertical lineto (y)
// convert to lineto to handle two-dimensional transforms
// convert to lineto to handle two-dimentional transforms
if (command === 'V') {

@@ -234,0 +239,0 @@ command = 'L';

@@ -1,3 +0,5 @@

export const name = 'cleanupAttrs';
export const description =
'use strict';
exports.name = 'cleanupAttrs';
exports.description =
'cleanups attributes from newlines, trailing and repeating spaces';

@@ -13,5 +15,6 @@

* @author Kir Belevich
* @type {import('./plugins-types.js').Plugin<'cleanupAttrs'>}
*
* @type {import('./plugins-types').Plugin<'cleanupAttrs'>}
*/
export const fn = (root, params) => {
exports.fn = (root, params) => {
const { newlines = true, trim = true, spaces = true } = params;

@@ -18,0 +21,0 @@ return {

@@ -1,6 +0,8 @@

import * as csstree from 'css-tree';
import { visit } from '../lib/xast.js';
'use strict';
export const name = 'cleanupEnableBackground';
export const description =
const csstree = require('css-tree');
const { visit } = require('../lib/xast.js');
exports.name = 'cleanupEnableBackground';
exports.description =
'remove or cleanup enable-background attribute when possible';

@@ -20,5 +22,5 @@

* @author Kir Belevich
* @type {import('./plugins-types.js').Plugin<'cleanupEnableBackground'>}
* @type {import('./plugins-types').Plugin<'cleanupEnableBackground'>}
*/
export const fn = (root) => {
exports.fn = (root) => {
let hasFilter = false;

@@ -25,0 +27,0 @@

@@ -1,11 +0,13 @@

import { visitSkip } from '../lib/xast.js';
import { hasScripts, findReferences } from '../lib/svgo/tools.js';
'use strict';
/**
* @typedef {import('../lib/types.js').XastElement} XastElement
* @typedef {import('../lib/types').XastElement} XastElement
*/
export const name = 'cleanupIds';
export const description = 'removes unused IDs and minifies used';
const { visitSkip } = require('../lib/xast.js');
const { hasScripts, findReferences } = require('../lib/svgo/tools');
exports.name = 'cleanupIds';
exports.description = 'removes unused IDs and minifies used';
const generateIdChars = [

@@ -122,5 +124,5 @@ 'a',

*
* @type {import('./plugins-types.js').Plugin<'cleanupIds'>}
* @type {import('./plugins-types').Plugin<'cleanupIds'>}
*/
export const fn = (_root, params) => {
exports.fn = (_root, params) => {
const {

@@ -236,5 +238,6 @@ remove = true,

// replace id in href and url()
element.attributes[name] = value
.replace(`#${encodeURI(id)}`, `#${currentIdString}`)
.replace(`#${id}`, `#${currentIdString}`);
element.attributes[name] = value.replace(
`#${encodeURI(id)}`,
`#${currentIdString}`,
);
} else {

@@ -241,0 +244,0 @@ // replace id in begin attribute

@@ -1,6 +0,8 @@

import { removeLeadingZero } from '../lib/svgo/tools.js';
'use strict';
export const name = 'cleanupListOfValues';
export const description = 'rounds list of values to the fixed precision';
const { removeLeadingZero } = require('../lib/svgo/tools.js');
exports.name = 'cleanupListOfValues';
exports.description = 'rounds list of values to the fixed precision';
const regNumericValues =

@@ -33,5 +35,5 @@ /^([-+]?\d*\.?\d+([eE][-+]?\d+)?)(px|pt|pc|mm|cm|m|in|ft|em|ex|%)?$/;

*
* @type {import('./plugins-types.js').Plugin<'cleanupListOfValues'>}
* @type {import('./plugins-types').Plugin<'cleanupListOfValues'>}
*/
export const fn = (_root, params) => {
exports.fn = (_root, params) => {
const {

@@ -38,0 +40,0 @@ floatPrecision = 3,

@@ -1,5 +0,7 @@

import { removeLeadingZero } from '../lib/svgo/tools.js';
'use strict';
export const name = 'cleanupNumericValues';
export const description =
const { removeLeadingZero } = require('../lib/svgo/tools');
exports.name = 'cleanupNumericValues';
exports.description =
'rounds numeric values to the fixed precision, removes default ‘px’ units';

@@ -26,5 +28,5 @@

*
* @type {import('./plugins-types.js').Plugin<'cleanupNumericValues'>}
* @type {import('./plugins-types').Plugin<'cleanupNumericValues'>}
*/
export const fn = (_root, params) => {
exports.fn = (_root, params) => {
const {

@@ -31,0 +33,0 @@ floatPrecision = 3,

@@ -1,12 +0,12 @@

import { computeStyle, collectStylesheet } from '../lib/style.js';
import { inheritableAttrs, elemsGroups } from './_collections.js';
'use strict';
/**
* @typedef {import('../lib/types.js').XastElement} XastElement
* @typedef {import('../lib/types.js').XastNode} XastNode
* @typedef {import('../lib/types').XastNode} XastNode
*/
export const name = 'collapseGroups';
export const description = 'collapses useless groups';
const { inheritableAttrs, elemsGroups } = require('./_collections.js');
exports.name = 'collapseGroups';
exports.description = 'collapses useless groups';
/**

@@ -52,7 +52,5 @@ * @type {(node: XastNode, name: string) => boolean}

*
* @type {import('./plugins-types.js').Plugin<'collapseGroups'>}
* @type {import('./plugins-types').Plugin<'collapseGroups'>}
*/
export const fn = (root) => {
const stylesheet = collectStylesheet(root);
exports.fn = () => {
return {

@@ -75,5 +73,2 @@ element: {

const firstChild = node.children[0];
const nodeHasFilter = !!(
node.attributes.filter || computeStyle(stylesheet, node).filter
);
// TODO untangle this mess

@@ -83,3 +78,3 @@ if (

firstChild.attributes.id == null &&
!nodeHasFilter &&
node.attributes.filter == null &&
(node.attributes.class == null ||

@@ -93,4 +88,2 @@ firstChild.attributes.class == null) &&

) {
const newChildElemAttrs = { ...firstChild.attributes };
for (const [name, value] of Object.entries(node.attributes)) {

@@ -101,19 +94,17 @@ // avoid copying to not conflict with animated attribute

}
if (newChildElemAttrs[name] == null) {
newChildElemAttrs[name] = value;
if (firstChild.attributes[name] == null) {
firstChild.attributes[name] = value;
} else if (name === 'transform') {
newChildElemAttrs[name] = value + ' ' + newChildElemAttrs[name];
} else if (newChildElemAttrs[name] === 'inherit') {
newChildElemAttrs[name] = value;
firstChild.attributes[name] =
value + ' ' + firstChild.attributes[name];
} else if (firstChild.attributes[name] === 'inherit') {
firstChild.attributes[name] = value;
} else if (
!inheritableAttrs.has(name) &&
newChildElemAttrs[name] !== value
inheritableAttrs.has(name) === false &&
firstChild.attributes[name] !== value
) {
return;
}
delete node.attributes[name];
}
node.attributes = {};
firstChild.attributes = newChildElemAttrs;
}

@@ -120,0 +111,0 @@ }

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

import { colorsNames, colorsProps, colorsShortNames } from './_collections.js';
import { includesUrlReference } from '../lib/svgo/tools.js';
'use strict';
export const name = 'convertColors';
export const description =
'converts colors: rgb() to #rrggbb and #rrggbb to #rgb';
const collections = require('./_collections.js');
exports.name = 'convertColors';
exports.description = 'converts colors: rgb() to #rrggbb and #rrggbb to #rgb';
const rNumber = '([+-]?(?:\\d*\\.\\d+|\\d+\\.?)%?)';

@@ -64,5 +64,5 @@ const rComma = '\\s*,\\s*';

*
* @type {import('./plugins-types.js').Plugin<'convertColors'>}
* @type {import('./plugins-types').Plugin<'convertColors'>}
*/
export const fn = (_root, params) => {
exports.fn = (_root, params) => {
const {

@@ -72,3 +72,2 @@ currentColor = false,

rgb2hex = true,
convertCase = 'lower',
shorthex = true,

@@ -82,3 +81,3 @@ shortname = true,

for (const [name, value] of Object.entries(node.attributes)) {
if (colorsProps.has(name)) {
if (collections.colorsProps.has(name)) {
let val = value;

@@ -97,3 +96,3 @@

if (matched) {
val = 'currentcolor';
val = 'currentColor';
}

@@ -105,4 +104,4 @@ }

const colorName = val.toLowerCase();
if (colorsNames[colorName] != null) {
val = colorsNames[colorName];
if (collections.colorsNames[colorName] != null) {
val = collections.colorsNames[colorName];
}

@@ -128,13 +127,5 @@ }

if (convertCase && !includesUrlReference(val)) {
if (convertCase === 'lower') {
val = val.toLowerCase();
} else if (convertCase === 'upper') {
val = val.toUpperCase();
}
}
// convert long hex to short hex
if (shorthex) {
let match = regHEX.exec(val);
let match = val.match(regHEX);
if (match != null) {

@@ -148,4 +139,4 @@ val = '#' + match[0][1] + match[0][3] + match[0][5];

const colorName = val.toLowerCase();
if (colorsShortNames[colorName] != null) {
val = colorsShortNames[colorName];
if (collections.colorsShortNames[colorName] != null) {
val = collections.colorsShortNames[colorName];
}

@@ -152,0 +143,0 @@ }

@@ -1,4 +0,6 @@

export const name = 'convertEllipseToCircle';
export const description = 'converts non-eccentric <ellipse>s to <circle>s';
'use strict';
exports.name = 'convertEllipseToCircle';
exports.description = 'converts non-eccentric <ellipse>s to <circle>s';
/**

@@ -11,5 +13,5 @@ * Converts non-eccentric <ellipse>s to <circle>s.

*
* @type {import('./plugins-types.js').Plugin<'convertEllipseToCircle'>}
* @type {import('./plugins-types').Plugin<'convertEllipseToCircle'>}
*/
export const fn = () => {
exports.fn = () => {
return {

@@ -16,0 +18,0 @@ element: {

@@ -1,16 +0,18 @@

import { attrsGroupsDefaults, colorsProps } from './_collections.js';
import {
'use strict';
/**
* @typedef {import('../lib/types').XastElement} XastElement
* @typedef {import('../lib/types').XastParent} XastParent
*/
const { attrsGroupsDefaults, colorsProps } = require('./_collections');
const {
detachNodeFromParent,
querySelectorAll,
querySelector,
} from '../lib/xast.js';
import { computeStyle, collectStylesheet } from '../lib/style.js';
} = require('../lib/xast');
const { computeStyle, collectStylesheet } = require('../lib/style');
/**
* @typedef {import('../lib/types.js').XastElement} XastElement
* @typedef {import('../lib/types.js').XastParent} XastParent
*/
export const name = 'convertOneStopGradients';
export const description =
exports.name = 'convertOneStopGradients';
exports.description =
'converts one-stop (single color) gradients to a plain color';

@@ -22,7 +24,7 @@

* @author Seth Falco <seth@falco.fun>
* @type {import('./plugins-types.js').Plugin<'convertOneStopGradients'>}
* @type {import('./plugins-types').Plugin<'convertOneStopGradients'>}
* @see https://developer.mozilla.org/docs/Web/SVG/Element/linearGradient
* @see https://developer.mozilla.org/docs/Web/SVG/Element/radialGradient
*/
export const fn = (root) => {
exports.fn = (root) => {
const stylesheet = collectStylesheet(root);

@@ -29,0 +31,0 @@

@@ -1,14 +0,16 @@

import { path2js, js2path } from './_path.js';
import { pathElems } from './_collections.js';
import { applyTransforms } from './applyTransforms.js';
import { collectStylesheet, computeStyle } from '../lib/style.js';
import { visit } from '../lib/xast.js';
import { cleanupOutData, toFixed } from '../lib/svgo/tools.js';
'use strict';
/**
* @typedef {import('../lib/types.js').PathDataItem} PathDataItem
* @typedef {import('../lib//types').PathDataItem} PathDataItem
*/
export const name = 'convertPathData';
export const description =
const { collectStylesheet, computeStyle } = require('../lib/style.js');
const { visit } = require('../lib/xast.js');
const { pathElems } = require('./_collections.js');
const { path2js, js2path } = require('./_path.js');
const { applyTransforms } = require('./applyTransforms.js');
const { cleanupOutData, toFixed } = require('../lib/svgo/tools');
exports.name = 'convertPathData';
exports.description =
'optimizes path data: writes in shorter form, applies transformations';

@@ -76,5 +78,5 @@

*
* @type {import('./plugins-types.js').Plugin<'convertPathData'>}
* @type {import('./plugins-types').Plugin<'convertPathData'>}
*/
export const fn = (root, params) => {
exports.fn = (root, params) => {
const {

@@ -176,9 +178,6 @@ // TODO convert to separate plugin in v3

let data = path2js(node);
var data = path2js(node);
// TODO: get rid of functions returns
if (data.length) {
const includesVertices = data.some(
(item) => item.command !== 'm' && item.command !== 'M',
);
convertToRelative(data);

@@ -196,19 +195,2 @@

const hasMarker =
node.attributes['marker-start'] != null ||
node.attributes['marker-end'] != null;
const isMarkersOnlyPath =
hasMarker &&
includesVertices &&
data.every(
(item) => item.command === 'm' || item.command === 'M',
);
if (isMarkersOnlyPath) {
data.push({
command: 'z',
args: [],
});
}
// @ts-ignore

@@ -420,2 +402,3 @@ js2path(node, data, newParams);

const qControlPoint = prevQControlPoint;
prevQControlPoint = undefined;

@@ -921,4 +904,2 @@ let command = item.command;

}
} else {
prevQControlPoint = undefined;
}

@@ -1141,3 +1122,3 @@ prev = item;

if (Math.abs(rx - ry) > error) return undefined;
const chord = Math.hypot(data[5], data[6]);
const chord = Math.sqrt(data[5] ** 2 + data[6] ** 2);
if (chord > rx * 2) return undefined;

@@ -1174,3 +1155,3 @@ return rx - Math.sqrt(rx ** 2 - 0.25 * chord ** 2);

function getDistance(point1, point2) {
return Math.hypot(point1[0] - point2[0], point1[1] - point2[1]);
return Math.sqrt((point1[0] - point2[0]) ** 2 + (point1[1] - point2[1]) ** 2);
}

@@ -1177,0 +1158,0 @@

@@ -1,11 +0,13 @@

import { stringifyPathData } from '../lib/path.js';
import { detachNodeFromParent } from '../lib/xast.js';
'use strict';
/**
* @typedef {import('../lib/types.js').PathDataItem} PathDataItem
* @typedef {import('../lib/types').PathDataItem} PathDataItem
*/
export const name = 'convertShapeToPath';
export const description = 'converts basic shapes to more compact path form';
const { stringifyPathData } = require('../lib/path.js');
const { detachNodeFromParent } = require('../lib/xast.js');
exports.name = 'convertShapeToPath';
exports.description = 'converts basic shapes to more compact path form';
const regNumber = /[-+]?(?:\d*\.\d+|\d+\.?)(?:[eE][-+]?\d+)?/g;

@@ -22,5 +24,5 @@

*
* @type {import('./plugins-types.js').Plugin<'convertShapeToPath'>}
* @type {import('./plugins-types').Plugin<'convertShapeToPath'>}
*/
export const fn = (root, params) => {
exports.fn = (root, params) => {
const { convertArcs = false, floatPrecision: precision } = params;

@@ -27,0 +29,0 @@

@@ -1,6 +0,8 @@

import { attrsGroups } from './_collections.js';
'use strict';
export const name = 'convertStyleToAttrs';
export const description = 'converts style to attributes';
const { attrsGroups } = require('./_collections');
exports.name = 'convertStyleToAttrs';
exports.description = 'converts style to attributes';
/**

@@ -66,5 +68,5 @@ * @type {(...args: string[]) => string}

*
* @type {import('./plugins-types.js').Plugin<'convertStyleToAttrs'>}
* @type {import('./plugins-types').Plugin<'convertStyleToAttrs'>}
*/
export const fn = (_root, params) => {
exports.fn = (_root, params) => {
const { keepImportant = false } = params;

@@ -71,0 +73,0 @@ return {

@@ -1,19 +0,19 @@

import {
js2transform,
matrixToTransform,
roundTransform,
transform2js,
transformsMultiply,
} from './_transforms.js';
'use strict';
/**
* @typedef {import('../lib/types.js').XastChild} XastChild
* @typedef {import('../lib/types.js').XastElement} XastElement
* @typedef {import('../lib/types.js').XastParent} XastParent
* @typedef {import('../lib/types').XastChild} XastChild
* @typedef {import('../lib/types').XastElement} XastElement
* @typedef {import('../lib/types').XastParent} XastParent
*/
export const name = 'convertTransform';
export const description =
'collapses multiple transformations and optimizes it';
const { cleanupOutData, toFixed } = require('../lib/svgo/tools.js');
const {
transform2js,
transformsMultiply,
matrixToTransform,
} = require('./_transforms.js');
exports.name = 'convertTransform';
exports.description = 'collapses multiple transformations and optimizes it';
/**

@@ -29,5 +29,5 @@ * Convert matrices to the short aliases,

*
* @type {import('./plugins-types.js').Plugin<'convertTransform'>}
* @type {import('./plugins-types').Plugin<'convertTransform'>}
*/
export const fn = (_root, params) => {
exports.fn = (_root, params) => {
const {

@@ -176,2 +176,38 @@ convertToShorts = true,

/**
* @type {(data: number[], params: TransformParams) => number[]}
*/
const degRound = (data, params) => {
if (
params.degPrecision != null &&
params.degPrecision >= 1 &&
params.floatPrecision < 20
) {
return smartRound(params.degPrecision, data);
} else {
return round(data);
}
};
/**
* @type {(data: number[], params: TransformParams) => number[]}
*/
const floatRound = (data, params) => {
if (params.floatPrecision >= 1 && params.floatPrecision < 20) {
return smartRound(params.floatPrecision, data);
} else {
return round(data);
}
};
/**
* @type {(data: number[], params: TransformParams) => number[]}
*/
const transformRound = (data, params) => {
if (params.transformPrecision >= 1 && params.floatPrecision < 20) {
return smartRound(params.transformPrecision, data);
} else {
return round(data);
}
};
/**
* Returns number of digits after the point. 0.125 → 3

@@ -299,1 +335,87 @@ *

};
/**
* Convert transforms JS representation to string.
*
* @param {TransformItem[]} transformJS
* @param {TransformParams} params
* @returns {string}
*/
const js2transform = (transformJS, params) => {
const transformString = transformJS
.map((transform) => {
roundTransform(transform, params);
return `${transform.name}(${cleanupOutData(transform.data, params)})`;
})
.join('');
return transformString;
};
/**
* @type {(transform: TransformItem, params: TransformParams) => TransformItem}
*/
const roundTransform = (transform, params) => {
switch (transform.name) {
case 'translate':
transform.data = floatRound(transform.data, params);
break;
case 'rotate':
transform.data = [
...degRound(transform.data.slice(0, 1), params),
...floatRound(transform.data.slice(1), params),
];
break;
case 'skewX':
case 'skewY':
transform.data = degRound(transform.data, params);
break;
case 'scale':
transform.data = transformRound(transform.data, params);
break;
case 'matrix':
transform.data = [
...transformRound(transform.data.slice(0, 4), params),
...floatRound(transform.data.slice(4), params),
];
break;
}
return transform;
};
/**
* Rounds numbers in array.
*
* @type {(data: number[]) => number[]}
*/
const round = (data) => {
return data.map(Math.round);
};
/**
* Decrease accuracy of floating-point numbers
* in transforms keeping a specified number of decimals.
* Smart rounds values like 2.349 to 2.35.
*
* @param {number} precision
* @param {number[]} data
* @returns {number[]}
*/
const smartRound = (precision, data) => {
for (
var i = data.length,
tolerance = +Math.pow(0.1, precision).toFixed(precision);
i--;
) {
if (toFixed(data[i], precision) !== data[i]) {
var rounded = +data[i].toFixed(precision - 1);
data[i] =
+Math.abs(rounded - data[i]).toFixed(precision + 1) >= tolerance
? +data[i].toFixed(precision)
: rounded;
}
}
return data;
};

@@ -1,19 +0,23 @@

import * as csstree from 'css-tree';
import { syntax } from 'csso';
import { attrsGroups, pseudoClasses } from './_collections.js';
import {
'use strict';
/**
* @typedef {import('../lib/types').XastElement} XastElement
* @typedef {import('../lib/types').XastParent} XastParent
*/
const csstree = require('css-tree');
const {
syntax: { specificity },
} = require('csso');
const {
visitSkip,
querySelectorAll,
detachNodeFromParent,
} from '../lib/xast.js';
import { compareSpecificity, includesAttrSelector } from '../lib/style.js';
} = require('../lib/xast.js');
const { compareSpecificity, includesAttrSelector } = require('../lib/style');
const { attrsGroups, pseudoClasses } = require('./_collections');
/**
* @typedef {import('../lib/types.js').XastElement} XastElement
* @typedef {import('../lib/types.js').XastParent} XastParent
*/
exports.name = 'inlineStyles';
exports.description = 'inline styles (additional options)';
export const name = 'inlineStyles';
export const description = 'inline styles (additional options)';
/**

@@ -37,6 +41,6 @@ * Some pseudo-classes can only be calculated by clients, like :visited,

*
* @type {import('./plugins-types.js').Plugin<'inlineStyles'>}
* @type {import('./plugins-types').Plugin<'inlineStyles'>}
* @author strarsis <strarsis@gmail.com>
*/
export const fn = (root, params) => {
exports.fn = (root, params) => {
const {

@@ -177,4 +181,4 @@ onlyMatchedOnce = true,

.sort((a, b) => {
const aSpecificity = syntax.specificity(a.item.data);
const bSpecificity = syntax.specificity(b.item.data);
const aSpecificity = specificity(a.item.data);
const bSpecificity = specificity(b.item.data);
return compareSpecificity(aSpecificity, bSpecificity);

@@ -181,0 +185,0 @@ })

@@ -1,31 +0,14 @@

/**
* @typedef {import('../lib/types.js').ComputedStyles} ComputedStyles
* @typedef {import('../lib/types.js').StaticStyle} StaticStyle
* @typedef {import('../lib/types.js').DynamicStyle} DynamicStyle
* @typedef {import("../lib/types.js").PathDataItem} PathDataItem
* @typedef {import('../lib/types.js').XastChild} XastChild
* @typedef {import('../lib/types.js').XastElement} XastElement
*/
'use strict';
import { collectStylesheet, computeStyle } from '../lib/style.js';
import { path2js, js2path, intersects } from './_path.js';
import { includesUrlReference } from '../lib/svgo/tools.js';
export const name = 'mergePaths';
export const description = 'merges multiple paths in one if possible';
/**
* @param {ComputedStyles} computedStyle
* @param {string} attName
* @returns {boolean}
* @typedef {import("../lib/types").PathDataItem} PathDataItem
* @typedef {import('../lib/types').XastChild} XastChild
* @typedef {import('../lib/types').XastElement} XastElement
*/
function elementHasUrl(computedStyle, attName) {
const style = computedStyle[attName];
if (style?.type === 'static') {
return includesUrlReference(style.value);
}
const { collectStylesheet, computeStyle } = require('../lib/style.js');
const { path2js, js2path, intersects } = require('./_path.js');
return false;
}
exports.name = 'mergePaths';
exports.description = 'merges multiple paths in one if possible';

@@ -37,8 +20,8 @@ /**

*
* @type {import('./plugins-types.js').Plugin<'mergePaths'>}
* @type {import('./plugins-types').Plugin<'mergePaths'>}
*/
export const fn = (root, params) => {
exports.fn = (root, params) => {
const {
force = false,
floatPrecision = 3,
floatPrecision,
noSpaceAfterFlags = false, // a20 60 45 0 1 30 20 → a20 60 45 0130 20

@@ -105,9 +88,3 @@ } = params;

computedStyle['marker-mid'] ||
computedStyle['marker-end'] ||
computedStyle['clip-path'] ||
computedStyle['mask'] ||
computedStyle['mask-image'] ||
['fill', 'filter', 'stroke'].some((attName) =>
elementHasUrl(computedStyle, attName),
)
computedStyle['marker-end']
) {

@@ -114,0 +91,0 @@ if (prevPathData) {

@@ -1,11 +0,13 @@

import { visitSkip, detachNodeFromParent } from '../lib/xast.js';
'use strict';
/**
* @typedef {import('../lib/types.js').XastElement} XastElement
* @typedef {import('../lib/types.js').XastChild} XastChild
* @typedef {import('../lib/types').XastElement} XastElement
* @typedef {import('../lib/types').XastChild} XastChild
*/
export const name = 'mergeStyles';
export const description = 'merge multiple style elements into one';
const { visitSkip, detachNodeFromParent } = require('../lib/xast.js');
exports.name = 'mergeStyles';
exports.description = 'merge multiple style elements into one';
/**

@@ -16,5 +18,5 @@ * Merge multiple style elements into one.

*
* @type {import('./plugins-types.js').Plugin<'mergeStyles'>}
* @type {import('./plugins-types').Plugin<'mergeStyles'>}
*/
export const fn = () => {
exports.fn = () => {
/**

@@ -21,0 +23,0 @@ * @type {?XastElement}

@@ -0,12 +1,14 @@

'use strict';
/**
* @typedef {import('../lib/types.js').XastElement} XastElement
* @typedef {import('../lib/types.js').XastParent} XastParent
* @typedef {import('../lib/types').XastElement} XastElement
* @typedef {import('../lib/types').XastParent} XastParent
*/
import * as csso from 'csso';
import { detachNodeFromParent } from '../lib/xast.js';
import { hasScripts } from '../lib/svgo/tools.js';
const csso = require('csso');
const { detachNodeFromParent } = require('../lib/xast');
const { hasScripts } = require('../lib/svgo/tools');
export const name = 'minifyStyles';
export const description = 'minifies styles and removes unused styles';
exports.name = 'minifyStyles';
exports.description = 'minifies styles and removes unused styles';

@@ -17,5 +19,5 @@ /**

* @author strarsis <strarsis@gmail.com>
* @type {import('./plugins-types.js').Plugin<'minifyStyles'>}
* @type {import('./plugins-types').Plugin<'minifyStyles'>}
*/
export const fn = (_root, { usage, ...params }) => {
exports.fn = (_root, { usage, ...params }) => {
/** @type {Map<XastElement, XastParent>} */

@@ -22,0 +24,0 @@ const styleElements = new Map();

@@ -1,8 +0,9 @@

import { visit } from '../lib/xast.js';
import { inheritableAttrs, pathElems } from './_collections.js';
'use strict';
export const name = 'moveElemsAttrsToGroup';
export const description =
'Move common attributes of group children to the group';
const { visit } = require('../lib/xast.js');
const { inheritableAttrs, pathElems } = require('./_collections.js');
exports.name = 'moveElemsAttrsToGroup';
exports.description = 'Move common attributes of group children to the group';
/**

@@ -28,5 +29,5 @@ * Move common attributes of group children to the group

*
* @type {import('./plugins-types.js').Plugin<'moveElemsAttrsToGroup'>}
* @type {import('./plugins-types').Plugin<'moveElemsAttrsToGroup'>}
*/
export const fn = (root) => {
exports.fn = (root) => {
// find if any style element is present

@@ -90,5 +91,4 @@ let deoptimizedWithStyles = false;

// preserve transform on children when group has filter or clip-path or mask
// preserve transform on children when group has clip-path or mask
if (
node.attributes['filter'] != null ||
node.attributes['clip-path'] != null ||

@@ -95,0 +95,0 @@ node.attributes.mask != null

@@ -1,8 +0,9 @@

import { pathElems, referencesProps } from './_collections.js';
import { includesUrlReference } from '../lib/svgo/tools.js';
'use strict';
export const name = 'moveGroupAttrsToElems';
export const description =
'moves some group attributes to the content elements';
const { pathElems, referencesProps } = require('./_collections.js');
const { includesUrlReference } = require('../lib/svgo/tools.js');
exports.name = 'moveGroupAttrsToElems';
exports.description = 'moves some group attributes to the content elements';
const pathElemsWithGroupsAndText = [...pathElems, 'g', 'text'];

@@ -26,5 +27,5 @@

*
* @type {import('./plugins-types.js').Plugin<'moveGroupAttrsToElems'>}
* @type {import('./plugins-types').Plugin<'moveGroupAttrsToElems'>}
*/
export const fn = () => {
exports.fn = () => {
return {

@@ -31,0 +32,0 @@ element: {

@@ -5,3 +5,3 @@ import type {

XastElement,
} from '../lib/types.js';
} from '../lib/types';

@@ -33,3 +33,2 @@ type DefaultPlugins = {

rgb2hex?: boolean;
convertCase?: false | 'lower' | 'upper';
shorthex?: boolean;

@@ -149,5 +148,2 @@ shortname?: boolean;

};
removeDeprecatedAttrs: {
removeUnsafe?: boolean;
};
removeDesc: {

@@ -279,6 +275,4 @@ removeAny?: boolean;

addClassesToSVGElement: {
className?: string | ((node: XastElement, info: PluginInfo) => string);
classNames?: Array<
string | ((node: XastElement, info: PluginInfo) => string)
>;
className?: string;
classNames?: string[];
};

@@ -285,0 +279,0 @@ removeAttributesBySelector: any;

@@ -1,12 +0,14 @@

import * as csstree from 'css-tree';
import { referencesProps } from './_collections.js';
'use strict';
/**
* @typedef {import('../lib/types.js').PluginInfo} PluginInfo
* @typedef {import('../lib/types.js').XastElement} XastElement
* @typedef {import('../lib/types').XastElement} XastElement
*/
export const name = 'prefixIds';
export const description = 'prefix IDs';
const csstree = require('css-tree');
const { referencesProps } = require('./_collections.js');
exports.name = 'prefixIds';
exports.description = 'prefix IDs';
/**

@@ -119,5 +121,5 @@ * extract basename from path

* @author strarsis <strarsis@gmail.com>
* @type {import('./plugins-types.js').Plugin<'prefixIds'>}
* @type {import('./plugins-types').Plugin<'prefixIds'>}
*/
export const fn = (_root, params, info) => {
exports.fn = (_root, params, info) => {
const {

@@ -187,2 +189,3 @@ delim = '__',

child.value = csstree.generate(cssAst);
return;
}

@@ -189,0 +192,0 @@ }

@@ -1,39 +0,41 @@

import { createPreset } from '../lib/svgo/plugins.js';
import * as removeDoctype from './removeDoctype.js';
import * as removeXMLProcInst from './removeXMLProcInst.js';
import * as removeComments from './removeComments.js';
import * as removeDeprecatedAttrs from './removeDeprecatedAttrs.js';
import * as removeMetadata from './removeMetadata.js';
import * as removeEditorsNSData from './removeEditorsNSData.js';
import * as cleanupAttrs from './cleanupAttrs.js';
import * as mergeStyles from './mergeStyles.js';
import * as inlineStyles from './inlineStyles.js';
import * as minifyStyles from './minifyStyles.js';
import * as cleanupIds from './cleanupIds.js';
import * as removeUselessDefs from './removeUselessDefs.js';
import * as cleanupNumericValues from './cleanupNumericValues.js';
import * as convertColors from './convertColors.js';
import * as removeUnknownsAndDefaults from './removeUnknownsAndDefaults.js';
import * as removeNonInheritableGroupAttrs from './removeNonInheritableGroupAttrs.js';
import * as removeUselessStrokeAndFill from './removeUselessStrokeAndFill.js';
import * as removeViewBox from './removeViewBox.js';
import * as cleanupEnableBackground from './cleanupEnableBackground.js';
import * as removeHiddenElems from './removeHiddenElems.js';
import * as removeEmptyText from './removeEmptyText.js';
import * as convertShapeToPath from './convertShapeToPath.js';
import * as convertEllipseToCircle from './convertEllipseToCircle.js';
import * as moveElemsAttrsToGroup from './moveElemsAttrsToGroup.js';
import * as moveGroupAttrsToElems from './moveGroupAttrsToElems.js';
import * as collapseGroups from './collapseGroups.js';
import * as convertPathData from './convertPathData.js';
import * as convertTransform from './convertTransform.js';
import * as removeEmptyAttrs from './removeEmptyAttrs.js';
import * as removeEmptyContainers from './removeEmptyContainers.js';
import * as mergePaths from './mergePaths.js';
import * as removeUnusedNS from './removeUnusedNS.js';
import * as sortAttrs from './sortAttrs.js';
import * as sortDefsChildren from './sortDefsChildren.js';
import * as removeTitle from './removeTitle.js';
import * as removeDesc from './removeDesc.js';
'use strict';
const { createPreset } = require('../lib/svgo/plugins.js');
const removeDoctype = require('./removeDoctype.js');
const removeXMLProcInst = require('./removeXMLProcInst.js');
const removeComments = require('./removeComments.js');
const removeMetadata = require('./removeMetadata.js');
const removeEditorsNSData = require('./removeEditorsNSData.js');
const cleanupAttrs = require('./cleanupAttrs.js');
const mergeStyles = require('./mergeStyles.js');
const inlineStyles = require('./inlineStyles.js');
const minifyStyles = require('./minifyStyles.js');
const cleanupIds = require('./cleanupIds.js');
const removeUselessDefs = require('./removeUselessDefs.js');
const cleanupNumericValues = require('./cleanupNumericValues.js');
const convertColors = require('./convertColors.js');
const removeUnknownsAndDefaults = require('./removeUnknownsAndDefaults.js');
const removeNonInheritableGroupAttrs = require('./removeNonInheritableGroupAttrs.js');
const removeUselessStrokeAndFill = require('./removeUselessStrokeAndFill.js');
const removeViewBox = require('./removeViewBox.js');
const cleanupEnableBackground = require('./cleanupEnableBackground.js');
const removeHiddenElems = require('./removeHiddenElems.js');
const removeEmptyText = require('./removeEmptyText.js');
const convertShapeToPath = require('./convertShapeToPath.js');
const convertEllipseToCircle = require('./convertEllipseToCircle.js');
const moveElemsAttrsToGroup = require('./moveElemsAttrsToGroup.js');
const moveGroupAttrsToElems = require('./moveGroupAttrsToElems.js');
const collapseGroups = require('./collapseGroups.js');
const convertPathData = require('./convertPathData.js');
const convertTransform = require('./convertTransform.js');
const removeEmptyAttrs = require('./removeEmptyAttrs.js');
const removeEmptyContainers = require('./removeEmptyContainers.js');
const mergePaths = require('./mergePaths.js');
const removeUnusedNS = require('./removeUnusedNS.js');
const sortAttrs = require('./sortAttrs.js');
const sortDefsChildren = require('./sortDefsChildren.js');
const removeTitle = require('./removeTitle.js');
const removeDesc = require('./removeDesc.js');
const presetDefault = createPreset({

@@ -45,3 +47,2 @@ name: 'preset-default',

removeComments,
removeDeprecatedAttrs,
removeMetadata,

@@ -82,2 +83,2 @@ removeEditorsNSData,

export default presetDefault;
module.exports = presetDefault;

@@ -1,5 +0,7 @@

import { querySelectorAll } from '../lib/xast.js';
'use strict';
export const name = 'removeAttributesBySelector';
export const description =
const { querySelectorAll } = require('../lib/xast.js');
exports.name = 'removeAttributesBySelector';
exports.description =
'removes attributes of elements that match a css selector';

@@ -74,5 +76,5 @@

*
* @type {import('./plugins-types.js').Plugin<'removeAttributesBySelector'>}
* @type {import('./plugins-types').Plugin<'removeAttributesBySelector'>}
*/
export const fn = (root, params) => {
exports.fn = (root, params) => {
const selectors = Array.isArray(params.selectors)

@@ -79,0 +81,0 @@ ? params.selectors

@@ -1,4 +0,6 @@

export const name = 'removeAttrs';
export const description = 'removes specified attributes';
'use strict';
exports.name = 'removeAttrs';
exports.description = 'removes specified attributes';
const DEFAULT_SEPARATOR = ':';

@@ -82,5 +84,5 @@ const ENOATTRS = `Warning: The plugin "removeAttrs" requires the "attrs" parameter.

*
* @type {import('./plugins-types.js').Plugin<'removeAttrs'>}
* @type {import('./plugins-types').Plugin<'removeAttrs'>}
*/
export const fn = (root, params) => {
exports.fn = (root, params) => {
if (typeof params.attrs == 'undefined') {

@@ -126,7 +128,10 @@ console.warn(ENOATTRS);

for (const [name, value] of Object.entries(node.attributes)) {
const isCurrentColor = value.toLowerCase() === 'currentcolor';
const isFillCurrentColor =
preserveCurrentColor && name == 'fill' && isCurrentColor;
preserveCurrentColor &&
name == 'fill' &&
value == 'currentColor';
const isStrokeCurrentColor =
preserveCurrentColor && name == 'stroke' && isCurrentColor;
preserveCurrentColor &&
name == 'stroke' &&
value == 'currentColor';
if (

@@ -133,0 +138,0 @@ !isFillCurrentColor &&

@@ -1,6 +0,8 @@

import { detachNodeFromParent } from '../lib/xast.js';
'use strict';
export const name = 'removeComments';
export const description = 'removes comments';
const { detachNodeFromParent } = require('../lib/xast.js');
exports.name = 'removeComments';
exports.description = 'removes comments';
/**

@@ -21,5 +23,5 @@ * If a comment matches one of the following patterns, it will be

*
* @type {import('./plugins-types.js').Plugin<'removeComments'>}
* @type {import('./plugins-types').Plugin<'removeComments'>}
*/
export const fn = (_root, params) => {
exports.fn = (_root, params) => {
const { preservePatterns = DEFAULT_PRESERVE_PATTERNS } = params;

@@ -26,0 +28,0 @@

@@ -1,6 +0,8 @@

import { detachNodeFromParent } from '../lib/xast.js';
'use strict';
export const name = 'removeDesc';
export const description = 'removes <desc>';
const { detachNodeFromParent } = require('../lib/xast.js');
exports.name = 'removeDesc';
exports.description = 'removes <desc>';
const standardDescs = /^(Created with|Created using)/;

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

*
* @type {import('./plugins-types.js').Plugin<'removeDesc'>}
* @type {import('./plugins-types').Plugin<'removeDesc'>}
*/
export const fn = (root, params) => {
exports.fn = (root, params) => {
const { removeAny = false } = params;

@@ -22,0 +24,0 @@ return {

@@ -1,3 +0,5 @@

export const name = 'removeDimensions';
export const description =
'use strict';
exports.name = 'removeDimensions';
exports.description =
'removes width and height in presence of viewBox (opposite to removeViewBox, disable it first)';

@@ -15,5 +17,5 @@

*
* @type {import('./plugins-types.js').Plugin<'removeDimensions'>}
* @type {import('./plugins-types').Plugin<'removeDimensions'>}
*/
export const fn = () => {
exports.fn = () => {
return {

@@ -20,0 +22,0 @@ element: {

@@ -1,6 +0,8 @@

import { detachNodeFromParent } from '../lib/xast.js';
'use strict';
export const name = 'removeDoctype';
export const description = 'removes doctype declaration';
const { detachNodeFromParent } = require('../lib/xast.js');
exports.name = 'removeDoctype';
exports.description = 'removes doctype declaration';
/**

@@ -28,5 +30,5 @@ * Remove DOCTYPE declaration.

*
* @type {import('./plugins-types.js').Plugin<'removeDoctype'>}
* @type {import('./plugins-types').Plugin<'removeDoctype'>}
*/
export const fn = () => {
exports.fn = () => {
return {

@@ -33,0 +35,0 @@ doctype: {

@@ -1,8 +0,9 @@

import { editorNamespaces } from './_collections.js';
import { detachNodeFromParent } from '../lib/xast.js';
'use strict';
export const name = 'removeEditorsNSData';
export const description =
'removes editors namespaces, elements and attributes';
const { detachNodeFromParent } = require('../lib/xast.js');
const { editorNamespaces } = require('./_collections.js');
exports.name = 'removeEditorsNSData';
exports.description = 'removes editors namespaces, elements and attributes';
/**

@@ -18,5 +19,5 @@ * Remove editors namespaces, elements and attributes.

*
* @type {import('./plugins-types.js').Plugin<'removeEditorsNSData'>}
* @type {import('./plugins-types').Plugin<'removeEditorsNSData'>}
*/
export const fn = (_root, params) => {
exports.fn = (_root, params) => {
let namespaces = [...editorNamespaces];

@@ -23,0 +24,0 @@ if (Array.isArray(params.additionalNamespaces)) {

@@ -1,5 +0,7 @@

import { detachNodeFromParent } from '../lib/xast.js';
'use strict';
export const name = 'removeElementsByAttr';
export const description =
const { detachNodeFromParent } = require('../lib/xast.js');
exports.name = 'removeElementsByAttr';
exports.description =
'removes arbitrary elements by ID or className (disabled by default)';

@@ -38,5 +40,5 @@

*
* @type {import('./plugins-types.js').Plugin<'removeElementsByAttr'>}
* @type {import('./plugins-types').Plugin<'removeElementsByAttr'>}
*/
export const fn = (root, params) => {
exports.fn = (root, params) => {
const ids =

@@ -43,0 +45,0 @@ params.id == null ? [] : Array.isArray(params.id) ? params.id : [params.id];

@@ -1,6 +0,8 @@

import { attrsGroups } from './_collections.js';
'use strict';
export const name = 'removeEmptyAttrs';
export const description = 'removes empty attributes';
const { attrsGroups } = require('./_collections.js');
exports.name = 'removeEmptyAttrs';
exports.description = 'removes empty attributes';
/**

@@ -11,5 +13,5 @@ * Remove attributes with empty values.

*
* @type {import('./plugins-types.js').Plugin<'removeEmptyAttrs'>}
* @type {import('./plugins-types').Plugin<'removeEmptyAttrs'>}
*/
export const fn = () => {
exports.fn = () => {
return {

@@ -16,0 +18,0 @@ element: {

@@ -1,7 +0,9 @@

import { elemsGroups } from './_collections.js';
import { detachNodeFromParent } from '../lib/xast.js';
'use strict';
export const name = 'removeEmptyContainers';
export const description = 'removes empty container elements';
const { detachNodeFromParent } = require('../lib/xast.js');
const { elemsGroups } = require('./_collections.js');
exports.name = 'removeEmptyContainers';
exports.description = 'removes empty container elements';
/**

@@ -20,5 +22,5 @@ * Remove empty containers.

*
* @type {import('./plugins-types.js').Plugin<'removeEmptyContainers'>}
* @type {import('./plugins-types').Plugin<'removeEmptyContainers'>}
*/
export const fn = () => {
exports.fn = () => {
return {

@@ -25,0 +27,0 @@ element: {

@@ -1,6 +0,8 @@

import { detachNodeFromParent } from '../lib/xast.js';
'use strict';
export const name = 'removeEmptyText';
export const description = 'removes empty <text> elements';
const { detachNodeFromParent } = require('../lib/xast.js');
exports.name = 'removeEmptyText';
exports.description = 'removes empty <text> elements';
/**

@@ -23,5 +25,5 @@ * Remove empty Text elements.

*
* @type {import('./plugins-types.js').Plugin<'removeEmptyText'>}
* @type {import('./plugins-types').Plugin<'removeEmptyText'>}
*/
export const fn = (root, params) => {
exports.fn = (root, params) => {
const { text = true, tspan = true, tref = true } = params;

@@ -28,0 +30,0 @@ return {

@@ -0,9 +1,11 @@

'use strict';
/**
* @typedef {import('../lib/types.js').XastChild} XastChild
* @typedef {import('../lib/types.js').XastElement} XastElement
* @typedef {import('../lib/types.js').XastParent} XastParent
* @typedef {import('../lib/types').XastChild} XastChild
* @typedef {import('../lib/types').XastElement} XastElement
* @typedef {import('../lib/types').XastParent} XastParent
*/
import { elemsGroups } from './_collections.js';
import {
const { elemsGroups } = require('./_collections.js');
const {
visit,

@@ -13,11 +15,11 @@ visitSkip,

detachNodeFromParent,
} from '../lib/xast.js';
import { collectStylesheet, computeStyle } from '../lib/style.js';
import { parsePathData } from '../lib/path.js';
import { hasScripts, findReferences } from '../lib/svgo/tools.js';
} = require('../lib/xast.js');
const { collectStylesheet, computeStyle } = require('../lib/style.js');
const { parsePathData } = require('../lib/path.js');
const { hasScripts, findReferences } = require('../lib/svgo/tools.js');
const nonRendering = elemsGroups.nonRendering;
export const name = 'removeHiddenElems';
export const description =
exports.name = 'removeHiddenElems';
exports.description =
'removes hidden elements (zero sized, with absent attributes)';

@@ -40,5 +42,5 @@

*
* @type {import('./plugins-types.js').Plugin<'removeHiddenElems'>}
* @type {import('./plugins-types').Plugin<'removeHiddenElems'>}
*/
export const fn = (root, params) => {
exports.fn = (root, params) => {
const {

@@ -97,19 +99,2 @@ isHidden = true,

/**
* Nodes can't be removed if they or any of their children have an id attribute that is referenced.
* @param {XastElement} node
* @returns boolean
*/
function canRemoveNonRenderingNode(node) {
if (allReferences.has(node.attributes.id)) {
return false;
}
for (const child of node.children) {
if (child.type === 'element' && !canRemoveNonRenderingNode(child)) {
return false;
}
}
return true;
}
/**
* @param {XastChild} node

@@ -136,2 +121,7 @@ * @param {XastParent} parentNode

if (nonRendering.has(node.name)) {
if (node.attributes.id == null) {
detachNodeFromParent(node, parentNode);
return visitSkip;
}
nonRenderedNodes.set(node, parentNode);

@@ -150,6 +140,2 @@ return visitSkip;

) {
if (node.name === 'path') {
nonRenderedNodes.set(node, parentNode);
return visitSkip;
}
removeElement(node, parentNode);

@@ -443,3 +429,5 @@ }

] of nonRenderedNodes.entries()) {
if (canRemoveNonRenderingNode(nonRenderedNode)) {
const id = nonRenderedNode.attributes.id;
if (!allReferences.has(id)) {
detachNodeFromParent(nonRenderedNode, nonRenderedParent);

@@ -446,0 +434,0 @@ }

@@ -1,6 +0,8 @@

import { detachNodeFromParent } from '../lib/xast.js';
'use strict';
export const name = 'removeMetadata';
export const description = 'removes <metadata>';
const { detachNodeFromParent } = require('../lib/xast.js');
exports.name = 'removeMetadata';
exports.description = 'removes <metadata>';
/**

@@ -13,5 +15,5 @@ * Remove <metadata>.

*
* @type {import('./plugins-types.js').Plugin<'removeMetadata'>}
* @type {import('./plugins-types').Plugin<'removeMetadata'>}
*/
export const fn = () => {
exports.fn = () => {
return {

@@ -18,0 +20,0 @@ element: {

@@ -1,9 +0,11 @@

import {
'use strict';
const {
inheritableAttrs,
attrsGroups,
presentationNonInheritableGroupAttrs,
} from './_collections.js';
} = require('./_collections');
export const name = 'removeNonInheritableGroupAttrs';
export const description =
exports.name = 'removeNonInheritableGroupAttrs';
exports.description =
'removes non-inheritable group’s presentational attributes';

@@ -16,5 +18,5 @@

*
* @type {import('./plugins-types.js').Plugin<'removeNonInheritableGroupAttrs'>}
* @type {import('./plugins-types').Plugin<'removeNonInheritableGroupAttrs'>}
*/
export const fn = () => {
exports.fn = () => {
return {

@@ -21,0 +23,0 @@ element: {

@@ -0,11 +1,13 @@

'use strict';
/**
* @typedef {import('../lib/types.js').PathDataItem} PathDataItem
* @typedef {import('../lib/types').PathDataItem} PathDataItem
*/
import { visitSkip, detachNodeFromParent } from '../lib/xast.js';
import { parsePathData } from '../lib/path.js';
import { intersects } from './_path.js';
const { visitSkip, detachNodeFromParent } = require('../lib/xast.js');
const { parsePathData } = require('../lib/path.js');
const { intersects } = require('./_path.js');
export const name = 'removeOffCanvasPaths';
export const description =
exports.name = 'removeOffCanvasPaths';
exports.description =
'removes elements that are drawn outside of the viewbox (disabled by default)';

@@ -18,5 +20,5 @@

*
* @type {import('./plugins-types.js').Plugin<'removeOffCanvasPaths'>}
* @type {import('./plugins-types').Plugin<'removeOffCanvasPaths'>}
*/
export const fn = () => {
exports.fn = () => {
/**

@@ -23,0 +25,0 @@ * @type {?{

@@ -1,6 +0,8 @@

import { detachNodeFromParent } from '../lib/xast.js';
'use strict';
export const name = 'removeRasterImages';
export const description = 'removes raster images (disabled by default)';
const { detachNodeFromParent } = require('../lib/xast.js');
exports.name = 'removeRasterImages';
exports.description = 'removes raster images (disabled by default)';
/**

@@ -13,5 +15,5 @@ * Remove raster images references in <image>.

*
* @type {import('./plugins-types.js').Plugin<'removeRasterImages'>}
* @type {import('./plugins-types').Plugin<'removeRasterImages'>}
*/
export const fn = () => {
exports.fn = () => {
return {

@@ -18,0 +20,0 @@ element: {

@@ -1,7 +0,9 @@

import { attrsGroups } from './_collections.js';
import { detachNodeFromParent } from '../lib/xast.js';
'use strict';
export const name = 'removeScriptElement';
export const description = 'removes scripts (disabled by default)';
const { detachNodeFromParent } = require('../lib/xast.js');
const { attrsGroups } = require('./_collections.js');
exports.name = 'removeScriptElement';
exports.description = 'removes scripts (disabled by default)';
/** Union of all event attributes. */

@@ -22,5 +24,5 @@ const eventAttrs = [

* @author Patrick Klingemann
* @type {import('./plugins-types.js').Plugin<'removeScriptElement'>}
* @type {import('./plugins-types').Plugin<'removeScriptElement'>}
*/
export const fn = () => {
exports.fn = () => {
return {

@@ -55,6 +57,3 @@ element: {

const index = parentNode.children.indexOf(node);
const usefulChildren = node.children.filter(
(child) => !(child.type === 'text' && /\s*/.test(child.value)),
);
parentNode.children.splice(index, 1, ...usefulChildren);
parentNode.children.splice(index, 1, ...node.children);

@@ -61,0 +60,0 @@ // TODO remove legacy parentNode in v4

@@ -1,6 +0,8 @@

import { detachNodeFromParent } from '../lib/xast.js';
'use strict';
export const name = 'removeStyleElement';
export const description = 'removes <style> element (disabled by default)';
const { detachNodeFromParent } = require('../lib/xast.js');
exports.name = 'removeStyleElement';
exports.description = 'removes <style> element (disabled by default)';
/**

@@ -13,5 +15,5 @@ * Remove <style>.

*
* @type {import('./plugins-types.js').Plugin<'removeStyleElement'>}
* @type {import('./plugins-types').Plugin<'removeStyleElement'>}
*/
export const fn = () => {
exports.fn = () => {
return {

@@ -18,0 +20,0 @@ element: {

@@ -1,6 +0,8 @@

import { detachNodeFromParent } from '../lib/xast.js';
'use strict';
export const name = 'removeTitle';
export const description = 'removes <title>';
const { detachNodeFromParent } = require('../lib/xast.js');
exports.name = 'removeTitle';
exports.description = 'removes <title>';
/**

@@ -13,5 +15,5 @@ * Remove <title>.

*
* @type {import('./plugins-types.js').Plugin<'removeTitle'>}
* @type {import('./plugins-types').Plugin<'removeTitle'>}
*/
export const fn = () => {
exports.fn = () => {
return {

@@ -18,0 +20,0 @@ element: {

@@ -1,2 +0,6 @@

import {
'use strict';
const { visitSkip, detachNodeFromParent } = require('../lib/xast.js');
const { collectStylesheet, computeStyle } = require('../lib/style.js');
const {
elems,

@@ -7,8 +11,6 @@ attrsGroups,

presentationNonInheritableGroupAttrs,
} from './_collections.js';
import { visitSkip, detachNodeFromParent } from '../lib/xast.js';
import { collectStylesheet, computeStyle } from '../lib/style.js';
} = require('./_collections');
export const name = 'removeUnknownsAndDefaults';
export const description =
exports.name = 'removeUnknownsAndDefaults';
exports.description =
'removes unknown elements content and attributes, removes attrs with default values';

@@ -94,5 +96,5 @@

*
* @type {import('./plugins-types.js').Plugin<'removeUnknownsAndDefaults'>}
* @type {import('./plugins-types').Plugin<'removeUnknownsAndDefaults'>}
*/
export const fn = (root, params) => {
exports.fn = (root, params) => {
const {

@@ -99,0 +101,0 @@ unknownContent = true,

@@ -1,4 +0,6 @@

export const name = 'removeUnusedNS';
export const description = 'removes unused namespaces declaration';
'use strict';
exports.name = 'removeUnusedNS';
exports.description = 'removes unused namespaces declaration';
/**

@@ -10,5 +12,5 @@ * Remove unused namespaces declaration from svg element

*
* @type {import('./plugins-types.js').Plugin<'removeUnusedNS'>}
* @type {import('./plugins-types').Plugin<'removeUnusedNS'>}
*/
export const fn = () => {
exports.fn = () => {
/**

@@ -15,0 +17,0 @@ * @type {Set<string>}

@@ -1,11 +0,13 @@

import { detachNodeFromParent } from '../lib/xast.js';
import { elemsGroups } from './_collections.js';
'use strict';
/**
* @typedef {import('../lib/types.js').XastElement} XastElement
* @typedef {import('../lib/types').XastElement} XastElement
*/
export const name = 'removeUselessDefs';
export const description = 'removes elements in <defs> without id';
const { detachNodeFromParent } = require('../lib/xast.js');
const { elemsGroups } = require('./_collections.js');
exports.name = 'removeUselessDefs';
exports.description = 'removes elements in <defs> without id';
/**

@@ -16,13 +18,9 @@ * Removes content of defs and properties that aren't rendered directly without ids.

*
* @type {import('./plugins-types.js').Plugin<'removeUselessDefs'>}
* @type {import('./plugins-types').Plugin<'removeUselessDefs'>}
*/
export const fn = () => {
exports.fn = () => {
return {
element: {
enter: (node, parentNode) => {
if (
node.name === 'defs' ||
(elemsGroups.nonRendering.has(node.name) &&
node.attributes.id == null)
) {
if (node.name === 'defs') {
/**

@@ -44,2 +42,7 @@ * @type {XastElement[]}

node.children = usefulNodes;
} else if (
elemsGroups.nonRendering.has(node.name) &&
node.attributes.id == null
) {
detachNodeFromParent(node, parentNode);
}

@@ -46,0 +49,0 @@ },

@@ -1,9 +0,11 @@

import { visit, visitSkip, detachNodeFromParent } from '../lib/xast.js';
import { collectStylesheet, computeStyle } from '../lib/style.js';
import { hasScripts } from '../lib/svgo/tools.js';
import { elemsGroups } from './_collections.js';
'use strict';
export const name = 'removeUselessStrokeAndFill';
export const description = 'removes useless stroke and fill attributes';
const { visit, visitSkip, detachNodeFromParent } = require('../lib/xast.js');
const { collectStylesheet, computeStyle } = require('../lib/style.js');
const { hasScripts } = require('../lib/svgo/tools.js');
const { elemsGroups } = require('./_collections.js');
exports.name = 'removeUselessStrokeAndFill';
exports.description = 'removes useless stroke and fill attributes';
/**

@@ -14,5 +16,5 @@ * Remove useless stroke and fill attrs.

*
* @type {import('./plugins-types.js').Plugin<'removeUselessStrokeAndFill'>}
* @type {import('./plugins-types').Plugin<'removeUselessStrokeAndFill'>}
*/
export const fn = (root, params) => {
exports.fn = (root, params) => {
const {

@@ -19,0 +21,0 @@ stroke: removeStroke = true,

@@ -1,4 +0,6 @@

export const name = 'removeViewBox';
export const description = 'removes viewBox attribute when possible';
'use strict';
exports.name = 'removeViewBox';
exports.description = 'removes viewBox attribute when possible';
const viewBoxElems = new Set(['pattern', 'svg', 'symbol']);

@@ -18,5 +20,5 @@

*
* @type {import('./plugins-types.js').Plugin<'removeViewBox'>}
* @type {import('./plugins-types').Plugin<'removeViewBox'>}
*/
export const fn = () => {
exports.fn = () => {
return {

@@ -23,0 +25,0 @@ element: {

@@ -1,9 +0,11 @@

import { elems } from './_collections.js';
'use strict';
const { elems } = require('./_collections');
/**
* @typedef {import('../lib/types.js').XastElement} XastElement
* @typedef {import('../lib/types').XastElement} XastElement
*/
export const name = 'removeXlink';
export const description =
exports.name = 'removeXlink';
exports.description =
'remove xlink namespace and replaces attributes with the SVG 2 equivalent where applicable';

@@ -59,6 +61,6 @@

*
* @type {import('./plugins-types.js').Plugin<'removeXlink'>}
* @type {import('./plugins-types').Plugin<'removeXlink'>}
* @see https://developer.mozilla.org/docs/Web/SVG/Attribute/xlink:href
*/
export const fn = (_, params) => {
exports.fn = (_, params) => {
const { includeLegacy } = params;

@@ -65,0 +67,0 @@

@@ -1,3 +0,5 @@

export const name = 'removeXMLNS';
export const description =
'use strict';
exports.name = 'removeXMLNS';
exports.description =
'removes xmlns attribute (for inline svg, disabled by default)';

@@ -15,5 +17,5 @@

*
* @type {import('./plugins-types.js').Plugin<'removeXMLNS'>}
* @type {import('./plugins-types').Plugin<'removeXMLNS'>}
*/
export const fn = () => {
exports.fn = () => {
return {

@@ -20,0 +22,0 @@ element: {

@@ -1,6 +0,8 @@

import { detachNodeFromParent } from '../lib/xast.js';
'use strict';
export const name = 'removeXMLProcInst';
export const description = 'removes XML processing instructions';
const { detachNodeFromParent } = require('../lib/xast.js');
exports.name = 'removeXMLProcInst';
exports.description = 'removes XML processing instructions';
/**

@@ -14,5 +16,5 @@ * Remove XML Processing Instruction.

*
* @type {import('./plugins-types.js').Plugin<'removeXMLProcInst'>}
* @type {import('./plugins-types').Plugin<'removeXMLProcInst'>}
*/
export const fn = () => {
exports.fn = () => {
return {

@@ -19,0 +21,0 @@ instruction: {

@@ -1,12 +0,14 @@

import { collectStylesheet } from '../lib/style.js';
import { detachNodeFromParent, querySelectorAll } from '../lib/xast.js';
'use strict';
const { collectStylesheet } = require('../lib/style');
const { detachNodeFromParent, querySelectorAll } = require('../lib/xast');
/**
* @typedef {import('../lib/types.js').XastElement} XastElement
* @typedef {import('../lib/types.js').XastParent} XastParent
* @typedef {import('../lib/types.js').XastNode} XastNode
* @typedef {import('../lib/types').XastElement} XastElement
* @typedef {import('../lib/types').XastParent} XastParent
* @typedef {import('../lib/types').XastNode} XastNode
*/
export const name = 'reusePaths';
export const description =
exports.name = 'reusePaths';
exports.description =
'Finds <path> elements with the same d, fill, and ' +

@@ -22,5 +24,5 @@ 'stroke, and converts them to <use> elements ' +

*
* @type {import('./plugins-types.js').Plugin<'reusePaths'>}
* @type {import('./plugins-types').Plugin<'reusePaths'>}
*/
export const fn = (root) => {
exports.fn = (root) => {
const stylesheet = collectStylesheet(root);

@@ -27,0 +29,0 @@

@@ -1,4 +0,6 @@

export const name = 'sortAttrs';
export const description = 'Sort element attributes for better compression';
'use strict';
exports.name = 'sortAttrs';
exports.description = 'Sort element attributes for better compression';
/**

@@ -9,5 +11,5 @@ * Sort element attributes for better compression

*
* @type {import('./plugins-types.js').Plugin<'sortAttrs'>}
* @type {import('./plugins-types').Plugin<'sortAttrs'>}
*/
export const fn = (_root, params) => {
exports.fn = (_root, params) => {
const {

@@ -14,0 +16,0 @@ order = [

@@ -1,4 +0,6 @@

export const name = 'sortDefsChildren';
export const description = 'Sorts children of <defs> to improve compression';
'use strict';
exports.name = 'sortDefsChildren';
exports.description = 'Sorts children of <defs> to improve compression';
/**

@@ -10,5 +12,5 @@ * Sorts children of defs in order to improve compression.

*
* @type {import('./plugins-types.js').Plugin<'sortDefsChildren'>}
* @type {import('./plugins-types').Plugin<'sortDefsChildren'>}
*/
export const fn = () => {
exports.fn = () => {
return {

@@ -15,0 +17,0 @@ element: {

@@ -52,8 +52,8 @@ <div align="center">

SVGO reads the configuration from `svgo.config.mjs` or the `--config path/to/config.mjs` command-line option. Some other parameters can be configured though command-line options too.
SVGO reads the configuration from `svgo.config.js` or the `--config path/to/config.js` command-line option. Some other parameters can be configured though command-line options too.
**`svgo.config.mjs`**
**`svgo.config.js`**
```js
export default {
module.exports = {
multipass: false, // boolean

@@ -84,6 +84,6 @@ datauri: 'base64', // 'base64'|'enc'|'unenc'

**`svgo.config.mjs`**
**`svgo.config.js`**
```js
export default {
module.exports = {
plugins: [

@@ -114,8 +114,8 @@ {

**`svgo.config.mjs`**
**`svgo.config.js`**
```js
import importedPlugin from './imported-plugin';
const importedPlugin = require('./imported-plugin');
export default {
module.exports = {
plugins: [

@@ -146,3 +146,3 @@ // plugin imported from another JavaScript file

```js
import { optimize } from 'svgo';
const { optimize } = require('svgo');

@@ -159,6 +159,6 @@ const result = optimize(svgString, {

If you write a tool on top of SVGO you may want to resolve the `svgo.config.mjs` file.
If you write a tool on top of SVGO you may want to resolve the `svgo.config.js` file.
```js
import { loadConfig } from 'svgo';
const { loadConfig } = require('svgo');

@@ -165,0 +165,0 @@ const config = await loadConfig();

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

SocketSocket SOC 2 Logo

Product

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

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc