1
0
mirror of https://github.com/svg/svgo.git synced 2025-08-01 18:46:52 +03:00

chore: apply more eslint rules (#2116)

This commit is contained in:
Seth Falco
2025-05-02 10:00:37 +01:00
committed by GitHub
parent 97079fb57e
commit a8a53dbd90
37 changed files with 336 additions and 267 deletions

View File

@ -34,6 +34,8 @@ export default [
{
files: ['**/*.js', '**/*.mjs'],
rules: {
'one-var': ['error', 'never'],
curly: 'error',
strict: 'error',
},
},

View File

@ -158,7 +158,7 @@ export const parseSvg = (data, from) => {
sax.onopentag = (data) => {
/** @type {XastElement} */
let element = {
const element = {
type: 'element',
name: data.name,
attributes: {},

View File

@ -1,7 +1,7 @@
import * as csstree from 'css-tree';
import * as csswhat from 'css-what';
import { syntax } from 'csso';
import { visit, matches } from './xast.js';
import { matches, visit } from './xast.js';
import {
attrsGroups,
inheritableAttrs,

View File

@ -3,12 +3,12 @@ import fs from 'fs/promises';
import path from 'path';
import {
VERSION,
optimize as optimizeAgnostic,
_collections,
builtinPlugins,
mapNodesToParents,
optimize as optimizeAgnostic,
querySelector,
querySelectorAll,
_collections,
} from './svgo.js';
/**

View File

@ -1,6 +1,6 @@
import os from 'os';
import path from 'path';
import { optimize, loadConfig } from './svgo-node.js';
import { loadConfig, optimize } from './svgo-node.js';
const describeLF = os.EOL === '\r\n' ? describe.skip : describe;
const describeCRLF = os.EOL === '\r\n' ? describe : describe.skip;

View File

@ -2,7 +2,7 @@ import fs from 'fs';
import path from 'path';
import colors from 'picocolors';
import { fileURLToPath } from 'url';
import { encodeSVGDatauri, decodeSVGDatauri } from './tools.js';
import { decodeSVGDatauri, encodeSVGDatauri } from './tools.js';
import { loadConfig, optimize } from '../svgo-node.js';
import { builtin } from '../builtin.js';
import { SvgoParserError } from '../parser.js';
@ -151,7 +151,7 @@ async function action(args, opts, command) {
if (process?.versions?.node && PKG.engines.node) {
// @ts-expect-error We control this and ensure it is never null.
var nodeVersion = String(PKG.engines.node).match(/\d*(\.\d+)*/)[0];
const nodeVersion = String(PKG.engines.node).match(/\d*(\.\d+)*/)[0];
if (parseFloat(process.versions.node) < parseFloat(nodeVersion)) {
throw Error(
`${PKG.name} requires Node.js version ${nodeVersion} or higher.`,
@ -224,7 +224,7 @@ async function action(args, opts, command) {
if (input.length && input[0] != '-') {
if (output.length == 1 && checkIsDir(output[0])) {
const dir = output[0];
for (var i = 0; i < input.length; i++) {
for (let i = 0; i < input.length; i++) {
output[i] = checkIsDir(input[i])
? input[i]
: path.resolve(dir, path.basename(input[i]));
@ -245,7 +245,7 @@ async function action(args, opts, command) {
// --folder
if (opts.folder) {
var outputFolder = (output && output[0]) || opts.folder;
const outputFolder = (output && output[0]) || opts.folder;
await optimizeFolder(config, opts.folder, outputFolder);
}
@ -254,8 +254,8 @@ async function action(args, opts, command) {
// STDIN
if (input[0] === '-') {
return new Promise((resolve, reject) => {
var data = '',
file = output[0];
let data = '';
const file = output[0];
process.stdin
.on('data', (chunk) => (data += chunk))
@ -274,7 +274,7 @@ async function action(args, opts, command) {
// --string
} else if (opts.string) {
var data = decodeSVGDatauri(opts.string);
const data = decodeSVGDatauri(opts.string);
return processSVGData(config, null, data, output[0]);
}
@ -308,7 +308,7 @@ function optimizeFolder(config, dir, output) {
*/
function processDirectory(config, dir, files, output) {
// take only *.svg files, recursively if necessary
var svgFilesDescriptions = getFilesDescriptions(config, dir, files, output);
const svgFilesDescriptions = getFilesDescriptions(config, dir, files, output);
return svgFilesDescriptions.length
? Promise.all(
@ -396,8 +396,8 @@ function optimizeFile(config, file, output) {
* @return {Promise<any>}
*/
function processSVGData(config, info, data, output, input = undefined) {
var startTime = Date.now(),
prevFileSize = Buffer.byteLength(data, 'utf8');
const startTime = Date.now();
const prevFileSize = Buffer.byteLength(data, 'utf8');
let result;
try {
@ -413,8 +413,8 @@ function processSVGData(config, info, data, output, input = undefined) {
if (config.datauri) {
result.data = encodeSVGDatauri(result.data, config.datauri);
}
var resultFileSize = Buffer.byteLength(result.data, 'utf8'),
processingTime = Date.now() - startTime;
const resultFileSize = Buffer.byteLength(result.data, 'utf8');
const processingTime = Date.now() - startTime;
return writeOutput(input, output, result.data).then(
function () {

View File

@ -23,7 +23,7 @@ const regReferencesBegin = /(\w+)\.[a-zA-Z]/;
* @returns {string}
*/
export const encodeSVGDatauri = (str, type) => {
var prefix = 'data:image/svg+xml';
let prefix = 'data:image/svg+xml';
if (!type || type === 'base64') {
// base64
prefix += ';base64,';
@ -45,13 +45,15 @@ export const encodeSVGDatauri = (str, type) => {
* @returns {string}
*/
export const decodeSVGDatauri = (str) => {
var regexp = /data:image\/svg\+xml(;charset=[^;,]*)?(;base64)?,(.*)/;
var match = regexp.exec(str);
const regexp = /data:image\/svg\+xml(;charset=[^;,]*)?(;base64)?,(.*)/;
const match = regexp.exec(str);
// plain string
if (!match) return str;
if (!match) {
return str;
}
var data = match[3];
const data = match[3];
if (match[2]) {
// base64
@ -88,13 +90,17 @@ export const cleanupOutData = (data, params, command) => {
delimiter = ' ';
// no extra space in front of first number
if (i == 0) delimiter = '';
if (i == 0) {
delimiter = '';
}
// no extra space after arc command flags (large-arc and sweep flags)
// a20 60 45 0 1 30 20 → a20 60 45 0130 20
if (params.noSpaceAfterFlags && (command == 'A' || command == 'a')) {
var pos = i % 7;
if (pos == 4 || pos == 5) delimiter = '';
const pos = i % 7;
if (pos == 4 || pos == 5) {
delimiter = '';
}
}
// remove floating-point numbers leading zeros

View File

@ -1,13 +1,13 @@
import { selectAll, selectOne, is } from 'css-select';
import { is, selectAll, selectOne } from 'css-select';
import { createAdapter } from './svgo/css-select-adapter.js';
/**
* @typedef {import('css-select').Options<XastNode & { children?: any }, XastElement>} Options
* @typedef {import('./types.js').Visitor} Visitor
* @typedef {import('./types.js').XastChild} XastChild
* @typedef {import('./types.js').XastElement} XastElement
* @typedef {import('./types.js').XastNode} XastNode
* @typedef {import('./types.js').XastChild} XastChild
* @typedef {import('./types.js').XastParent} XastParent
* @typedef {import('./types.js').Visitor} Visitor
*/
/**

View File

@ -1,10 +1,10 @@
import { detachNodeFromParent, visit, visitSkip } from './xast.js';
/**
* @typedef {import('./types.js').XastElement} XastElement
* @typedef {import('./types.js').XastRoot} XastRoot
*/
import { visit, visitSkip, detachNodeFromParent } from './xast.js';
/**
* @param {XastElement[]} children
* @returns {XastRoot}

View File

@ -133,7 +133,7 @@
"pixelmatch": "^7.1.0",
"playwright": "^1.51.1",
"pngjs": "^7.0.0",
"prettier": "^3.2.5",
"prettier": "^3.5.3",
"rimraf": "^5.0.7",
"rollup": "^4.22.4",
"tar-stream": "^3.1.7",

View File

@ -24,7 +24,7 @@ import { parsePathData, stringifyPathData } from '../lib/path.js';
*/
/** @type {[number, number]} */
var prevCtrlPoint;
let prevCtrlPoint;
/**
* Convert path string to JS representation.
@ -34,7 +34,10 @@ var prevCtrlPoint;
*/
export const path2js = (path) => {
// @ts-expect-error legacy
if (path.pathJS) return path.pathJS;
if (path.pathJS) {
// @ts-expect-error legacy
return path.pathJS;
}
/** @type {PathDataItem[]} */
const pathData = []; // JS representation of the path data
const newPathData = parsePathData(path.attributes.d);
@ -59,8 +62,8 @@ export const path2js = (path) => {
const convertRelativeToAbsolute = (data) => {
/** @type {PathDataItem[]} */
const newData = [];
let start = [0, 0];
let cursor = [0, 0];
const start = [0, 0];
const cursor = [0, 0];
for (let { command, args } of data) {
args = args.slice();
@ -259,8 +262,9 @@ export const intersects = function (path1, path2) {
);
});
})
)
) {
return false;
}
// Get a convex hull from points of each subpath. Has the most complexity O(n·log n).
const hullNest1 = points1.list.map(convexHull);
@ -268,15 +272,19 @@ export const intersects = function (path1, path2) {
// Check intersection of every subpath of the first path with every subpath of the second.
return hullNest1.some(function (hull1) {
if (hull1.list.length < 3) return false;
if (hull1.list.length < 3) {
return false;
}
return hullNest2.some(function (hull2) {
if (hull2.list.length < 3) return false;
if (hull2.list.length < 3) {
return false;
}
var simplex = [getSupport(hull1, hull2, [1, 0])], // create the initial simplex
direction = minus(simplex[0]); // set the direction to point towards the origin
const simplex = [getSupport(hull1, hull2, [1, 0])]; // create the initial simplex
const direction = minus(simplex[0]); // set the direction to point towards the origin
var iterations = 1e4; // infinite loop protection, 10 000 iterations is more than enough
let iterations = 1e4; // infinite loop protection, 10 000 iterations is more than enough
while (true) {
if (iterations-- == 0) {
@ -288,9 +296,13 @@ export const intersects = function (path1, path2) {
// add a new point
simplex.push(getSupport(hull1, hull2, direction));
// see if the new point was on the correct side of the origin
if (dot(direction, simplex[simplex.length - 1]) <= 0) return false;
if (dot(direction, simplex[simplex.length - 1]) <= 0) {
return false;
}
// process the simplex
if (processSimplex(simplex, direction)) return true;
if (processSimplex(simplex, direction)) {
return true;
}
}
});
});
@ -316,16 +328,16 @@ export const intersects = function (path1, path2) {
* @returns {number[]}
*/
function supportPoint(polygon, direction) {
var index =
direction[1] >= 0
? direction[0] < 0
? polygon.maxY
: polygon.maxX
: direction[0] < 0
? polygon.minX
: polygon.minY,
max = -Infinity,
value;
let index =
direction[1] >= 0
? direction[0] < 0
? polygon.maxY
: polygon.maxX
: direction[0] < 0
? polygon.minX
: polygon.minY;
let max = -Infinity;
let value;
while ((value = dot(polygon.list[index], direction)) > max) {
max = value;
index = ++index % polygon.list.length;
@ -343,10 +355,10 @@ function processSimplex(simplex, direction) {
// we only need to handle to 1-simplex and 2-simplex
if (simplex.length == 2) {
// 1-simplex
let a = simplex[1],
b = simplex[0],
AO = minus(simplex[1]),
AB = sub(b, a);
const a = simplex[1];
const b = simplex[0];
const AO = minus(simplex[1]);
const AB = sub(b, a);
// AO is in the same direction as AB
if (dot(AO, AB) > 0) {
// get the vector perpendicular to AB facing O
@ -358,14 +370,14 @@ function processSimplex(simplex, direction) {
}
} else {
// 2-simplex
let a = simplex[2], // [a, b, c] = simplex
b = simplex[1],
c = simplex[0],
AB = sub(b, a),
AC = sub(c, a),
AO = minus(a),
ACB = orth(AB, AC), // the vector perpendicular to AB facing away from C
ABC = orth(AC, AB); // the vector perpendicular to AC facing away from B
const a = simplex[2]; // [a, b, c] = simplex
const b = simplex[1];
const c = simplex[0];
const AB = sub(b, a);
const AC = sub(c, a);
const AO = minus(a);
const ACB = orth(AB, AC); // the vector perpendicular to AB facing away from C
const ABC = orth(AC, AB); // the vector perpendicular to AC facing away from B
if (dot(ACB, AO) > 0) {
if (dot(AB, AO) > 0) {
@ -388,7 +400,9 @@ function processSimplex(simplex, direction) {
simplex.splice(0, 2); // simplex = [a]
}
} // region 7
else return true;
else {
return true;
}
}
return false;
}
@ -425,7 +439,7 @@ function dot(v1, v2) {
* @returns {number[]}
*/
function orth(v, from) {
var o = [-v[1], v[0]];
const o = [-v[1], v[0]];
return dot(o, minus(from)) < 0 ? minus(o) : o;
}
@ -477,10 +491,10 @@ function gatherPoints(pathData) {
points.list.length === 0
? { list: [], minX: 0, minY: 0, maxX: 0, maxY: 0 }
: points.list[points.list.length - 1];
let prev = i === 0 ? null : pathData[i - 1];
const prev = i === 0 ? null : pathData[i - 1];
let basePoint =
subPath.list.length === 0 ? null : subPath.list[subPath.list.length - 1];
let data = pathDataItem.args;
const data = pathDataItem.args;
let ctrlPoint = basePoint;
// TODO fix null hack
@ -580,7 +594,7 @@ function gatherPoints(pathData) {
if (basePoint != null) {
// Convert the arc to Bézier curves and use the same approximation
// @ts-expect-error no idea what's going on here
var curves = a2c.apply(0, basePoint.concat(data));
const curves = a2c.apply(0, basePoint.concat(data));
for (
var cData;
(cData = curves.splice(0, 6).map(toAbsolute)).length;
@ -600,14 +614,18 @@ function gatherPoints(pathData) {
0.5 * (cData[2] + cData[4]),
0.5 * (cData[3] + cData[5]),
]);
if (curves.length) addPoint(subPath, (basePoint = cData.slice(-2)));
if (curves.length) {
addPoint(subPath, (basePoint = cData.slice(-2)));
}
}
}
break;
}
// Save final command coordinates
if (data.length >= 2) addPoint(subPath, data.slice(-2));
if (data.length >= 2) {
addPoint(subPath, data.slice(-2));
}
}
return points;
@ -626,9 +644,9 @@ function convexHull(points) {
return a[0] == b[0] ? a[1] - b[1] : a[0] - b[0];
});
var lower = [],
minY = 0,
bottom = 0;
const lower = [];
let minY = 0;
let bottom = 0;
for (let i = 0; i < points.list.length; i++) {
while (
lower.length >= 2 &&
@ -644,9 +662,9 @@ function convexHull(points) {
lower.push(points.list[i]);
}
var upper = [],
maxY = points.list.length - 1,
top = 0;
const upper = [];
let maxY = points.list.length - 1;
let top = 0;
for (let i = points.list.length; i--; ) {
while (
upper.length >= 2 &&
@ -748,17 +766,17 @@ const a2c = (
y1 = rotateY(x1, y1, -rad);
x2 = rotateX(x2, y2, -rad);
y2 = rotateY(x2, y2, -rad);
var x = (x1 - x2) / 2,
y = (y1 - y2) / 2;
var h = (x * x) / (rx * rx) + (y * y) / (ry * ry);
const x = (x1 - x2) / 2;
const y = (y1 - y2) / 2;
let h = (x * x) / (rx * rx) + (y * y) / (ry * ry);
if (h > 1) {
h = Math.sqrt(h);
rx = h * rx;
ry = h * ry;
}
var rx2 = rx * rx;
var ry2 = ry * ry;
var k =
const rx2 = rx * rx;
const ry2 = ry * ry;
const k =
(large_arc_flag == sweep_flag ? -1 : 1) *
Math.sqrt(
Math.abs(
@ -786,11 +804,11 @@ const a2c = (
cx = recursive[2];
cy = recursive[3];
}
var df = f2 - f1;
let df = f2 - f1;
if (Math.abs(df) > _120) {
var f2old = f2,
x2old = x2,
y2old = y2;
const f2old = f2;
const x2old = x2;
const y2old = y2;
f2 = f1 + _120 * (sweep_flag && f2 > f1 ? 1 : -1);
x2 = cx + rx * Math.cos(f2);
y2 = cy + ry * Math.sin(f2);
@ -802,27 +820,27 @@ const a2c = (
]);
}
df = f2 - f1;
var c1 = Math.cos(f1),
s1 = Math.sin(f1),
c2 = Math.cos(f2),
s2 = Math.sin(f2),
t = Math.tan(df / 4),
hx = (4 / 3) * rx * t,
hy = (4 / 3) * ry * t,
m = [
-hx * s1,
hy * c1,
x2 + hx * s2 - x1,
y2 - hy * c2 - y1,
x2 - x1,
y2 - y1,
];
const c1 = Math.cos(f1);
const s1 = Math.sin(f1);
const c2 = Math.cos(f2);
const s2 = Math.sin(f2);
const t = Math.tan(df / 4);
const hx = (4 / 3) * rx * t;
const hy = (4 / 3) * ry * t;
const m = [
-hx * s1,
hy * c1,
x2 + hx * s2 - x1,
y2 - hy * c2 - y1,
x2 - x1,
y2 - y1,
];
if (recursive) {
return m.concat(res);
} else {
res = m.concat(res);
var newres = [];
for (var i = 0, n = res.length; i < n; i++) {
const newres = [];
for (let i = 0, n = res.length; i < n; i++) {
newres[i] =
i % 2
? rotateY(res[i - 1], res[i], rad)

View File

@ -514,10 +514,10 @@ const transformToMatrix = (transform) => {
];
case 'rotate':
// [cos(a), sin(a), -sin(a), cos(a), x, y]
var cos = mth.cos(transform.data[0]),
sin = mth.sin(transform.data[0]),
cx = transform.data[1] || 0,
cy = transform.data[2] || 0;
var cos = mth.cos(transform.data[0]);
var sin = mth.sin(transform.data[0]);
var cx = transform.data[1] || 0;
var cy = transform.data[2] || 0;
return [
cos,
sin,
@ -719,13 +719,13 @@ const round = (data) => {
*/
const smartRound = (precision, data) => {
for (
var i = data.length,
let 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);
const rounded = +data[i].toFixed(precision - 1);
data[i] =
+Math.abs(rounded - data[i]).toFixed(precision + 1) >= tolerance
? +data[i].toFixed(precision)

View File

@ -7,7 +7,7 @@
export const name = 'addAttributesToSVGElement';
export const description = 'adds attributes to an outer <svg> element';
var ENOCLS = `Error in plugin "addAttributesToSVGElement": absent parameters.
const ENOCLS = `Error in plugin "addAttributesToSVGElement": absent parameters.
It should have a list of "attributes" or one "attribute".
Config example:

View File

@ -10,7 +10,7 @@
export const name = 'addClassesToSVGElement';
export const description = 'adds classnames to an outer <svg> element';
var ENOCLS = `Error in plugin "addClassesToSVGElement": absent parameters.
const ENOCLS = `Error in plugin "addClassesToSVGElement": absent parameters.
It should have a list of classes in "classNames" or one "className".
Config example:

View File

@ -1,13 +1,13 @@
import { path2js } from './_path.js';
import {
transformsMultiply,
transform2js,
transformArc,
transformsMultiply,
} from './_transforms.js';
import { referencesProps, attrsGroupsDefaults } from './_collections.js';
import { attrsGroupsDefaults, referencesProps } from './_collections.js';
import { collectStylesheet, computeStyle } from '../lib/style.js';
import { removeLeadingZero, includesUrlReference } from '../lib/svgo/tools.js';
import { includesUrlReference, removeLeadingZero } from '../lib/svgo/tools.js';
/**
* @typedef {import('../lib/types.js').PathDataItem} PathDataItem

View File

@ -1,5 +1,5 @@
import { visitSkip } from '../lib/xast.js';
import { hasScripts, findReferences } from '../lib/svgo/tools.js';
import { findReferences, hasScripts } from '../lib/svgo/tools.js';
/**
* @typedef {import('../lib/types.js').XastElement} XastElement
@ -224,7 +224,7 @@ export const fn = (_root, params) => {
// replace referenced IDs with the minified ones
if (minify && isIdPreserved(id) === false) {
/** @type {?string} */
let currentIdString = null;
let currentIdString;
do {
currentId = generateId(currentId);
currentIdString = getIdString(currentId);

View File

@ -64,7 +64,7 @@ export const fn = (_root, params) => {
// round it to the fixed precision
let num = Number(Number(match[1]).toFixed(floatPrecision));
/** @type {any} */
let matchedUnit = match[3] || '';
const matchedUnit = match[3] || '';
/** @type {'' | keyof typeof absoluteLengths} */
let units = matchedUnit;

View File

@ -10,7 +10,7 @@ import { removeLeadingZero } from '../lib/svgo/tools.js';
export const name = 'cleanupNumericValues';
export const description =
'rounds numeric values to the fixed precision, removes default px units';
'rounds numeric values to the fixed precision, removes default "px" units';
const regNumericValues =
/^([-+]?\d*\.?\d+([eE][-+]?\d+)?)(px|pt|pc|mm|cm|m|in|ft|em|ex|%)?$/;
@ -26,8 +26,7 @@ const absoluteLengths = {
};
/**
* Round numeric values to the fixed precision,
* remove default 'px' units.
* Round numeric values to the fixed precision, remove default 'px' units.
*
* @author Kir Belevich
*
@ -69,7 +68,7 @@ export const fn = (_root, params) => {
// round it to the fixed precision
let num = Number(Number(match[1]).toFixed(floatPrecision));
/** @type {any} */
let matchedUnit = match[3] || '';
const matchedUnit = match[3] || '';
/** @type {'' | keyof typeof absoluteLengths} */
let units = matchedUnit;

View File

@ -1,5 +1,5 @@
import { computeStyle, collectStylesheet } from '../lib/style.js';
import { inheritableAttrs, elemsGroups } from './_collections.js';
import { collectStylesheet, computeStyle } from '../lib/style.js';
import { elemsGroups, inheritableAttrs } from './_collections.js';
/**
* @typedef {import('../lib/types.js').Plugin} Plugin

View File

@ -121,9 +121,9 @@ export const fn = (_root, params) => {
// convert rgb() to long hex
if (rgb2hex) {
let match = val.match(regRGB);
const match = val.match(regRGB);
if (match != null) {
let nums = match.slice(1, 4).map((m) => {
const nums = match.slice(1, 4).map((m) => {
let n;
if (m.indexOf('%') > -1) {
n = Math.round(parseFloat(m) * 2.55);
@ -146,7 +146,7 @@ export const fn = (_root, params) => {
// convert long hex to short hex
if (shorthex) {
let match = regHEX.exec(val);
const match = regHEX.exec(val);
if (match != null) {
val = '#' + match[0][1] + match[0][3] + match[0][5];
}

View File

@ -1,10 +1,10 @@
import { attrsGroupsDefaults, colorsProps } from './_collections.js';
import {
detachNodeFromParent,
querySelectorAll,
querySelector,
querySelectorAll,
} from '../lib/xast.js';
import { computeStyle, collectStylesheet } from '../lib/style.js';
import { collectStylesheet, computeStyle } from '../lib/style.js';
/**
* @typedef {import('../lib/types.js').Plugin} Plugin
@ -64,7 +64,7 @@ export const fn = (root) => {
});
const href = node.attributes['xlink:href'] || node.attributes['href'];
let effectiveNode =
const effectiveNode =
stops.length === 0 && href != null && href.startsWith('#')
? querySelector(root, href)
: node;

View File

@ -1,4 +1,4 @@
import { path2js, js2path } from './_path.js';
import { js2path, path2js } from './_path.js';
import { pathElems } from './_collections.js';
import { applyTransforms } from './applyTransforms.js';
import { collectStylesheet, computeStyle } from '../lib/style.js';
@ -217,8 +217,8 @@ export const fn = (root, params) => {
* @returns {PathDataItem[]}
*/
const convertToRelative = (pathData) => {
let start = [0, 0];
let cursor = [0, 0];
const start = [0, 0];
const cursor = [0, 0];
let prevCoords = [0, 0];
for (let i = 0; i < pathData.length; i += 1) {
@ -413,8 +413,8 @@ function filters(
let next = path[index + 1];
if (command !== 'Z' && command !== 'z') {
var sdata = data,
circle;
let sdata = data;
let circle;
if (command === 's') {
sdata = [0, 0].concat(data);
@ -434,31 +434,31 @@ function filters(
isConvex(sdata) &&
(circle = findCircle(sdata))
) {
var r = roundData([circle.radius])[0],
angle = findArcAngle(sdata, circle),
sweep = sdata[5] * sdata[0] - sdata[4] * sdata[1] > 0 ? 1 : 0,
/** @type {PathDataItem} */
arc = {
command: 'a',
args: [r, r, 0, 0, sweep, sdata[4], sdata[5]],
// @ts-expect-error
coords: item.coords.slice(),
// @ts-expect-error
base: item.base,
},
/** @type {PathDataItem[]} */
output = [arc],
// relative coordinates to adjust the found circle
/** @type {Point} */
relCenter = [
circle.center[0] - sdata[4],
circle.center[1] - sdata[5],
],
relCircle = { center: relCenter, radius: circle.radius },
arcCurves = [item],
hasPrev = 0,
suffix = '',
nextLonghand;
const r = roundData([circle.radius])[0];
let angle = findArcAngle(sdata, circle);
const sweep = sdata[5] * sdata[0] - sdata[4] * sdata[1] > 0 ? 1 : 0;
/** @type {PathDataItem} */
let arc = {
command: 'a',
args: [r, r, 0, 0, sweep, sdata[4], sdata[5]],
// @ts-expect-error
coords: item.coords.slice(),
// @ts-expect-error
base: item.base,
};
/** @type {PathDataItem[]} */
const output = [arc];
// relative coordinates to adjust the found circle
/** @type {Point} */
const relCenter = [
circle.center[0] - sdata[4],
circle.center[1] - sdata[5],
];
const relCircle = { center: relCenter, radius: circle.radius };
const arcCurves = [item];
let hasPrev = 0;
let suffix = '';
let nextLonghand;
if (
(prev.command == 'c' &&
@ -473,8 +473,8 @@ function filters(
arc.args[5] = arc.coords[0] - arc.base[0];
// @ts-expect-error
arc.args[6] = arc.coords[1] - arc.base[1];
var prevData = prev.command == 'a' ? prev.sdata : prev.args;
var prevAngle = findArcAngle(prevData, {
const prevData = prev.command == 'a' ? prev.sdata : prev.args;
const prevAngle = findArcAngle(prevData, {
center: [
prevData[4] + circle.center[0],
prevData[5] + circle.center[1],
@ -482,7 +482,9 @@ function filters(
radius: circle.radius,
});
angle += prevAngle;
if (angle > Math.PI) arc.args[3] = 1;
if (angle > Math.PI) {
arc.args[3] = 1;
}
hasPrev = 1;
}
@ -492,7 +494,7 @@ function filters(
(next = path[++j]) && (next.command === 'c' || next.command === 's');
) {
var nextData = next.args;
let nextData = next.args;
if (next.command == 's') {
nextLonghand = makeLonghand(
{ command: 's', args: next.args.slice() },
@ -504,8 +506,12 @@ function filters(
}
if (isConvex(nextData) && isArc(nextData, relCircle)) {
angle += findArcAngle(nextData, relCircle);
if (angle - 2 * Math.PI > 1e-3) break; // more than 360°
if (angle > Math.PI) arc.args[3] = 1;
if (angle - 2 * Math.PI > 1e-3) {
break;
} // more than 360°
if (angle > Math.PI) {
arc.args[3] = 1;
}
arcCurves.push(next);
if (2 * Math.PI - angle > 1e-3) {
// less than 360°
@ -550,7 +556,9 @@ function filters(
}
relCenter[0] -= nextData[4];
relCenter[1] -= nextData[5];
} else break;
} else {
break;
}
}
if ((stringify(output) + suffix).length < stringify(arcCurves).length) {
@ -558,7 +566,7 @@ function filters(
makeLonghand(path[j], path[j - 1].args);
}
if (hasPrev) {
var prevArc = output.shift();
const prevArc = output.shift();
// @ts-expect-error
roundData(prevArc.args);
// @ts-expect-error
@ -580,7 +588,9 @@ function filters(
// filter out consumed next items
path.splice(index + 1, arcCurves.length - 1 - hasPrev, ...output);
}
if (!arc) return false;
if (!arc) {
return false;
}
command = 'a';
data = arc.args;
// @ts-expect-error
@ -600,7 +610,7 @@ function filters(
command === 's' ||
command === 'c'
) {
for (var i = data.length; i--; ) {
for (let i = data.length; i--; ) {
// @ts-expect-error
data[i] += item.base[i % 2] - relSubpoint[i % 2];
}
@ -618,9 +628,11 @@ function filters(
}
roundData(data);
if (command == 'h') relSubpoint[0] += data[0];
else if (command == 'v') relSubpoint[1] += data[0];
else {
if (command == 'h') {
relSubpoint[0] += data[0];
} else if (command == 'v') {
relSubpoint[1] += data[0];
} else {
relSubpoint[0] += data[data.length - 2];
relSubpoint[1] += data[data.length - 1];
}
@ -656,11 +668,15 @@ function filters(
(command === 'c' && isCurveStraightLine(data)) ||
(command === 's' && isCurveStraightLine(sdata))
) {
if (next && next.command == 's') makeLonghand(next, data); // fix up next curve
if (next && next.command == 's') {
makeLonghand(next, data);
} // fix up next curve
command = 'l';
data = data.slice(-2);
} else if (command === 'q' && isCurveStraightLine(data)) {
if (next && next.command == 't') makeLonghand(next, data); // fix up next curve
if (next && next.command == 't') {
makeLonghand(next, data);
} // fix up next curve
command = 'l';
data = data.slice(-2);
} else if (
@ -708,12 +724,14 @@ function filters(
y1 + y2 - item.base[1],
);
roundData(newData);
const originalLength = cleanupOutData(data, params).length,
newLength = cleanupOutData(newData, params).length;
const originalLength = cleanupOutData(data, params).length;
const newLength = cleanupOutData(newData, params).length;
if (newLength < originalLength) {
command = 'q';
data = newData;
if (next && next.command == 's') makeLonghand(next, data); // fix up next curve
if (next && next.command == 's') {
makeLonghand(next, data);
} // fix up next curve
}
}
}
@ -877,7 +895,9 @@ function filters(
// z resets coordinates
relSubpoint[0] = pathBase[0];
relSubpoint[1] = pathBase[1];
if (prev.command === 'Z' || prev.command === 'z') return false;
if (prev.command === 'Z' || prev.command === 'z') {
return false;
}
}
if (
(command === 'Z' || command === 'z') &&
@ -887,8 +907,9 @@ function filters(
Math.abs(item.base[0] - item.coords[0]) < error / 10 &&
// @ts-expect-error
Math.abs(item.base[1] - item.coords[1]) < error / 10
)
) {
return false;
}
if (command === 'q') {
// @ts-expect-error
@ -919,19 +940,21 @@ function filters(
* @returns {PathDataItem[]}
*/
function convertToMixed(path, params) {
var prev = path[0];
let prev = path[0];
path = path.filter(function (item, index) {
if (index == 0) return true;
if (index == 0) {
return true;
}
if (item.command === 'Z' || item.command === 'z') {
prev = item;
return true;
}
var command = item.command,
data = item.args,
adata = data.slice(),
rdata = data.slice();
const command = item.command;
const data = item.args;
const adata = data.slice();
const rdata = data.slice();
if (
command === 'm' ||
@ -941,7 +964,7 @@ function convertToMixed(path, params) {
command === 's' ||
command === 'c'
) {
for (var i = adata.length; i--; ) {
for (let i = adata.length; i--; ) {
// @ts-expect-error
adata[i] += item.base[i % 2];
}
@ -961,8 +984,8 @@ function convertToMixed(path, params) {
roundData(adata);
roundData(rdata);
var absoluteDataStr = cleanupOutData(adata, params),
relativeDataStr = cleanupOutData(rdata, params);
const absoluteDataStr = cleanupOutData(adata, params);
const relativeDataStr = cleanupOutData(rdata, params);
// Convert to absolute coordinates if it's shorter or forceAbsolutePath is true.
// v-20 -> V0
@ -1002,7 +1025,7 @@ function convertToMixed(path, params) {
* @returns {boolean}
*/
function isConvex(data) {
var center = getIntersection([
const center = getIntersection([
0,
0,
data[2],
@ -1030,19 +1053,21 @@ function isConvex(data) {
*/
function getIntersection(coords) {
// Prev line equation parameters.
var a1 = coords[1] - coords[3], // y1 - y2
b1 = coords[2] - coords[0], // x2 - x1
c1 = coords[0] * coords[3] - coords[2] * coords[1], // x1 * y2 - x2 * y1
// Next line equation parameters
a2 = coords[5] - coords[7], // y1 - y2
b2 = coords[6] - coords[4], // x2 - x1
c2 = coords[4] * coords[7] - coords[5] * coords[6], // x1 * y2 - x2 * y1
denom = a1 * b2 - a2 * b1;
const a1 = coords[1] - coords[3]; // y1 - y2
const b1 = coords[2] - coords[0]; // x2 - x1
const c1 = coords[0] * coords[3] - coords[2] * coords[1]; // x1 * y2 - x2 * y1
// Next line equation parameters
const a2 = coords[5] - coords[7]; // y1 - y2
const b2 = coords[6] - coords[4]; // x2 - x1
const c2 = coords[4] * coords[7] - coords[5] * coords[6]; // x1 * y2 - x2 * y1
const denom = a1 * b2 - a2 * b1;
if (!denom) return; // parallel lines haven't an intersection
if (!denom) {
return;
} // parallel lines haven't an intersection
/** @type {Point} */
var cross = [(b1 * c2 - b2 * c1) / denom, (a1 * c2 - a2 * c1) / -denom];
const cross = [(b1 * c2 - b2 * c1) / denom, (a1 * c2 - a2 * c1) / -denom];
if (
!isNaN(cross[0]) &&
!isNaN(cross[1]) &&
@ -1083,7 +1108,7 @@ function strongRound(data) {
* @returns {number[]}
*/
function round(data) {
for (var i = data.length; i-- > 0; ) {
for (let i = data.length; i-- > 0; ) {
data[i] = Math.round(data[i]);
}
return data;
@ -1098,17 +1123,20 @@ function round(data) {
*/
function isCurveStraightLine(data) {
// Get line equation a·x + b·y + c = 0 coefficients a, b (c = 0) by start and end points.
var i = data.length - 2,
a = -data[i + 1], // y1 y2 (y1 = 0)
b = data[i], // x2 x1 (x1 = 0)
d = 1 / (a * a + b * b); // same part for all points
let i = data.length - 2;
const a = -data[i + 1]; // y1 y2 (y1 = 0)
const b = data[i]; // x2 x1 (x1 = 0)
const d = 1 / (a * a + b * b); // same part for all points
if (i <= 1 || !isFinite(d)) return false; // curve that ends at start point isn't the case
if (i <= 1 || !isFinite(d)) {
return false;
} // curve that ends at start point isn't the case
// Distance from point (x0, y0) to the line is sqrt((c a·x0 b·y0)² / (a² + b²))
while ((i -= 2) >= 0) {
if (Math.sqrt(Math.pow(a * data[i] + b * data[i + 1], 2) * d) > error)
if (Math.sqrt(Math.pow(a * data[i] + b * data[i + 1], 2) * d) > error) {
return false;
}
}
return true;
@ -1122,11 +1150,17 @@ function isCurveStraightLine(data) {
* @returns {number | undefined}
*/
function calculateSagitta(data) {
if (data[3] === 1) return undefined;
if (data[3] === 1) {
return undefined;
}
const [rx, ry] = data;
if (Math.abs(rx - ry) > error) return undefined;
if (Math.abs(rx - ry) > error) {
return undefined;
}
const chord = Math.hypot(data[5], data[6]);
if (chord > rx * 2) return undefined;
if (chord > rx * 2) {
return undefined;
}
return rx - Math.sqrt(rx ** 2 - 0.25 * chord ** 2);
}
@ -1185,10 +1219,10 @@ function reflectPoint(controlPoint, base) {
* @returns {Point}
*/
function getCubicBezierPoint(curve, t) {
var sqrT = t * t,
cubT = sqrT * t,
mt = 1 - t,
sqrMt = mt * mt;
const sqrT = t * t;
const cubT = sqrT * t;
const mt = 1 - t;
const sqrMt = mt * mt;
return [
3 * sqrMt * t * curve[0] + 3 * mt * sqrT * curve[2] + cubT * curve[4],
@ -1203,22 +1237,25 @@ function getCubicBezierPoint(curve, t) {
* @returns {Circle | undefined}
*/
function findCircle(curve) {
var midPoint = getCubicBezierPoint(curve, 1 / 2),
m1 = [midPoint[0] / 2, midPoint[1] / 2],
m2 = [(midPoint[0] + curve[4]) / 2, (midPoint[1] + curve[5]) / 2],
center = getIntersection([
m1[0],
m1[1],
m1[0] + m1[1],
m1[1] - m1[0],
m2[0],
m2[1],
m2[0] + (m2[1] - midPoint[1]),
m2[1] - (m2[0] - midPoint[0]),
]),
radius = center && getDistance([0, 0], center),
const midPoint = getCubicBezierPoint(curve, 1 / 2);
const m1 = [midPoint[0] / 2, midPoint[1] / 2];
const m2 = [(midPoint[0] + curve[4]) / 2, (midPoint[1] + curve[5]) / 2];
const center = getIntersection([
m1[0],
m1[1],
m1[0] + m1[1],
m1[1] - m1[0],
m2[0],
m2[1],
m2[0] + (m2[1] - midPoint[1]),
m2[1] - (m2[0] - midPoint[0]),
]);
const radius = center && getDistance([0, 0], center);
const tolerance = Math.min(
arcThreshold * error,
// @ts-expect-error
tolerance = Math.min(arcThreshold * error, (arcTolerance * radius) / 100);
(arcTolerance * radius) / 100,
);
if (
center &&
@ -1232,9 +1269,10 @@ function findCircle(curve) {
) <= tolerance
);
})
)
) {
// @ts-expect-error
return { center: center, radius: radius };
}
}
/**
@ -1245,7 +1283,7 @@ function findCircle(curve) {
* @returns {boolean}
*/
function isArc(curve, circle) {
var tolerance = Math.min(
const tolerance = Math.min(
arcThreshold * error,
(arcTolerance * circle.radius) / 100,
);
@ -1282,10 +1320,10 @@ function isArcPrev(curve, circle) {
* @returns {number}
*/
function findArcAngle(curve, relCircle) {
var x1 = -relCircle.center[0],
y1 = -relCircle.center[1],
x2 = curve[4] - relCircle.center[0],
y2 = curve[5] - relCircle.center[1];
const x1 = -relCircle.center[0];
const y1 = -relCircle.center[1];
const x2 = curve[4] - relCircle.center[0];
const y2 = curve[5] - relCircle.center[1];
return Math.acos(
(x1 * x2 + y1 * y2) / Math.sqrt((x1 * x1 + y1 * y1) * (x2 * x2 + y2 * y2)),
@ -1301,7 +1339,7 @@ function findArcAngle(curve, relCircle) {
*/
function data2Path(params, pathData) {
return pathData.reduce(function (pathString, item) {
var strData = '';
let strData = '';
if (item.args) {
strData = cleanupOutData(roundData(item.args.slice()), params);
}

View File

@ -46,7 +46,9 @@ export const fn = (root, params) => {
// Values like '100%' compute to NaN, thus running after
// cleanupNumericValues when 'px' units has already been removed.
// TODO: Calculate sizes from % and non-px units if possible.
if (Number.isNaN(x - y + width - height)) return;
if (Number.isNaN(x - y + width - height)) {
return;
}
/** @type {PathDataItem[]} */
const pathData = [
{ command: 'M', args: [x, y] },
@ -69,7 +71,9 @@ export const fn = (root, params) => {
const y1 = Number(node.attributes.y1 || '0');
const x2 = Number(node.attributes.x2 || '0');
const y2 = Number(node.attributes.y2 || '0');
if (Number.isNaN(x1 - y1 + x2 - y2)) return;
if (Number.isNaN(x1 - y1 + x2 - y2)) {
return;
}
/** @type {PathDataItem[]} */
const pathData = [
{ command: 'M', args: [x1, y1] },

View File

@ -111,8 +111,8 @@ export const fn = (_root, params) => {
if (styles.length) {
styles = styles.filter(function (style) {
if (style[0]) {
var prop = style[0].toLowerCase(),
val = style[1];
const prop = style[0].toLowerCase();
let val = style[1];
if (rQuotedString.test(val)) {
val = val.slice(1, -1);

View File

@ -205,12 +205,12 @@ const floatDigits = (n) => {
* @returns {TransformItem[]}
*/
const convertToShorts = (transforms, params) => {
for (var i = 0; i < transforms.length; i++) {
for (let i = 0; i < transforms.length; i++) {
let transform = transforms[i];
// convert matrix to the short aliases
if (params.matrixToTransform && transform.name === 'matrix') {
var decomposed = matrixToTransform(transform, params);
const decomposed = matrixToTransform(transform, params);
if (
js2transform(decomposed, params).length <=
js2transform([transform], params).length

View File

@ -2,9 +2,9 @@ import * as csstree from 'css-tree';
import { syntax } from 'csso';
import { attrsGroups, pseudoClasses } from './_collections.js';
import {
visitSkip,
querySelectorAll,
detachNodeFromParent,
querySelectorAll,
visitSkip,
} from '../lib/xast.js';
import { compareSpecificity, includesAttrSelector } from '../lib/style.js';
@ -72,7 +72,7 @@ export const fn = (root, params) => {
* matchedElements?: XastElement[]
* }[]}
*/
let selectors = [];
const selectors = [];
return {
element: {

View File

@ -1,5 +1,5 @@
import { collectStylesheet, computeStyle } from '../lib/style.js';
import { path2js, js2path, intersects } from './_path.js';
import { intersects, js2path, path2js } from './_path.js';
import { includesUrlReference } from '../lib/svgo/tools.js';
/**

View File

@ -1,4 +1,4 @@
import { visitSkip, detachNodeFromParent } from '../lib/xast.js';
import { detachNodeFromParent, visitSkip } from '../lib/xast.js';
/**
* @typedef {import('../lib/types.js').Plugin} Plugin

View File

@ -164,7 +164,7 @@ export const fn = (_root, params, info) => {
const cssText = child.value;
/** @type {?csstree.CssNode} */
let cssAst = null;
let cssAst;
try {
cssAst = csstree.parse(cssText, {
parseValue: true,

View File

@ -1,13 +1,13 @@
import { elemsGroups } from './_collections.js';
import {
detachNodeFromParent,
querySelector,
visit,
visitSkip,
querySelector,
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';
import { findReferences, hasScripts } from '../lib/svgo/tools.js';
/**
* @typedef {import('../lib/types.js').XastChild} XastChild
@ -183,7 +183,9 @@ export const fn = (root, params) => {
if (node.name === 'use') {
for (const attr of Object.keys(node.attributes)) {
if (attr !== 'href' && !attr.endsWith(':href')) continue;
if (attr !== 'href' && !attr.endsWith(':href')) {
continue;
}
const value = node.attributes[attr];
const id = value.slice(1);

View File

@ -1,6 +1,6 @@
import {
inheritableAttrs,
attrsGroups,
inheritableAttrs,
presentationNonInheritableGroupAttrs,
} from './_collections.js';
@ -10,7 +10,7 @@ import {
export const name = 'removeNonInheritableGroupAttrs';
export const description =
'removes non-inheritable groups presentational attributes';
"removes non-inheritable group's presentational attributes";
/**
* Remove non-inheritable group's "presentation" attributes.

View File

@ -1,4 +1,4 @@
import { visitSkip, detachNodeFromParent } from '../lib/xast.js';
import { detachNodeFromParent, visitSkip } from '../lib/xast.js';
import { parsePathData } from '../lib/path.js';
import { intersects } from './_path.js';

View File

@ -1,11 +1,11 @@
import {
elems,
attrsGroups,
elemsGroups,
attrsGroupsDefaults,
elems,
elemsGroups,
presentationNonInheritableGroupAttrs,
} from './_collections.js';
import { visitSkip, detachNodeFromParent } from '../lib/xast.js';
import { detachNodeFromParent, visitSkip } from '../lib/xast.js';
import { collectStylesheet, computeStyle } from '../lib/style.js';
/**

View File

@ -1,4 +1,4 @@
import { visit, visitSkip, detachNodeFromParent } from '../lib/xast.js';
import { detachNodeFromParent, visit, visitSkip } from '../lib/xast.js';
import { collectStylesheet, computeStyle } from '../lib/style.js';
import { hasScripts } from '../lib/svgo/tools.js';
import { elemsGroups } from './_collections.js';

View File

@ -110,7 +110,7 @@ describe('coa', function () {
tempFolder,
'--quiet',
]);
let optimizedWeight = calcFolderSvgWeight(svgFolderPath);
const optimizedWeight = calcFolderSvgWeight(svgFolderPath);
expect(optimizedWeight).toBeLessThanOrEqual(initWeight);
});

View File

@ -2,7 +2,7 @@ import fs from 'node:fs/promises';
import path from 'path';
import { EOL } from 'os';
import { fileURLToPath } from 'url';
import { VERSION, optimize, builtinPlugins } from '../../lib/svgo.js';
import { VERSION, builtinPlugins, optimize } from '../../lib/svgo.js';
const __dirname = path.dirname(fileURLToPath(import.meta.url));

View File

@ -3952,12 +3952,12 @@ __metadata:
languageName: node
linkType: hard
"prettier@npm:^3.2.5":
version: 3.2.5
resolution: "prettier@npm:3.2.5"
"prettier@npm:^3.5.3":
version: 3.5.3
resolution: "prettier@npm:3.5.3"
bin:
prettier: bin/prettier.cjs
checksum: 2ee4e1417572372afb7a13bb446b34f20f1bf1747db77cf6ccaf57a9be005f2f15c40f903d41a6b79eec3f57fff14d32a20fb6dee1f126da48908926fe43c311
checksum: 61e97bb8e71a95d8f9c71f1fd5229c9aaa9d1e184dedb12399f76aa802fb6fdc8954ecac9df25a7f82ee7311cf8ddbd06baf5507388fc98e5b44036cc6a88a1b
languageName: node
linkType: hard
@ -4546,7 +4546,7 @@ __metadata:
pixelmatch: ^7.1.0
playwright: ^1.51.1
pngjs: ^7.0.0
prettier: ^3.2.5
prettier: ^3.5.3
rimraf: ^5.0.7
rollup: ^4.22.4
sax: ^1.4.1