1
0
mirror of https://github.com/svg/svgo.git synced 2025-07-31 07:44:22 +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'], files: ['**/*.js', '**/*.mjs'],
rules: { rules: {
'one-var': ['error', 'never'],
curly: 'error',
strict: 'error', strict: 'error',
}, },
}, },

View File

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

View File

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

View File

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

View File

@ -1,6 +1,6 @@
import os from 'os'; import os from 'os';
import path from 'path'; 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 describeLF = os.EOL === '\r\n' ? describe.skip : describe;
const describeCRLF = os.EOL === '\r\n' ? describe : describe.skip; const describeCRLF = os.EOL === '\r\n' ? describe : describe.skip;

View File

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

View File

@ -23,7 +23,7 @@ const regReferencesBegin = /(\w+)\.[a-zA-Z]/;
* @returns {string} * @returns {string}
*/ */
export const encodeSVGDatauri = (str, type) => { export const encodeSVGDatauri = (str, type) => {
var prefix = 'data:image/svg+xml'; let prefix = 'data:image/svg+xml';
if (!type || type === 'base64') { if (!type || type === 'base64') {
// base64 // base64
prefix += ';base64,'; prefix += ';base64,';
@ -45,13 +45,15 @@ export const encodeSVGDatauri = (str, type) => {
* @returns {string} * @returns {string}
*/ */
export const decodeSVGDatauri = (str) => { export const decodeSVGDatauri = (str) => {
var regexp = /data:image\/svg\+xml(;charset=[^;,]*)?(;base64)?,(.*)/; const regexp = /data:image\/svg\+xml(;charset=[^;,]*)?(;base64)?,(.*)/;
var match = regexp.exec(str); const match = regexp.exec(str);
// plain string // plain string
if (!match) return str; if (!match) {
return str;
}
var data = match[3]; const data = match[3];
if (match[2]) { if (match[2]) {
// base64 // base64
@ -88,13 +90,17 @@ export const cleanupOutData = (data, params, command) => {
delimiter = ' '; delimiter = ' ';
// no extra space in front of first number // 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) // no extra space after arc command flags (large-arc and sweep flags)
// a20 60 45 0 1 30 20 → a20 60 45 0130 20 // a20 60 45 0 1 30 20 → a20 60 45 0130 20
if (params.noSpaceAfterFlags && (command == 'A' || command == 'a')) { if (params.noSpaceAfterFlags && (command == 'A' || command == 'a')) {
var pos = i % 7; const pos = i % 7;
if (pos == 4 || pos == 5) delimiter = ''; if (pos == 4 || pos == 5) {
delimiter = '';
}
} }
// remove floating-point numbers leading zeros // 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'; import { createAdapter } from './svgo/css-select-adapter.js';
/** /**
* @typedef {import('css-select').Options<XastNode & { children?: any }, XastElement>} Options * @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').XastElement} XastElement
* @typedef {import('./types.js').XastNode} XastNode * @typedef {import('./types.js').XastNode} XastNode
* @typedef {import('./types.js').XastChild} XastChild
* @typedef {import('./types.js').XastParent} XastParent * @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').XastElement} XastElement
* @typedef {import('./types.js').XastRoot} XastRoot * @typedef {import('./types.js').XastRoot} XastRoot
*/ */
import { visit, visitSkip, detachNodeFromParent } from './xast.js';
/** /**
* @param {XastElement[]} children * @param {XastElement[]} children
* @returns {XastRoot} * @returns {XastRoot}

View File

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

View File

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

View File

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

View File

@ -7,7 +7,7 @@
export const name = 'addAttributesToSVGElement'; export const name = 'addAttributesToSVGElement';
export const description = 'adds attributes to an outer <svg> element'; 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". It should have a list of "attributes" or one "attribute".
Config example: Config example:

View File

@ -10,7 +10,7 @@
export const name = 'addClassesToSVGElement'; export const name = 'addClassesToSVGElement';
export const description = 'adds classnames to an outer <svg> element'; 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". It should have a list of classes in "classNames" or one "className".
Config example: Config example:

View File

@ -1,13 +1,13 @@
import { path2js } from './_path.js'; import { path2js } from './_path.js';
import { import {
transformsMultiply,
transform2js, transform2js,
transformArc, transformArc,
transformsMultiply,
} from './_transforms.js'; } from './_transforms.js';
import { referencesProps, attrsGroupsDefaults } from './_collections.js'; import { attrsGroupsDefaults, referencesProps } from './_collections.js';
import { collectStylesheet, computeStyle } from '../lib/style.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 * @typedef {import('../lib/types.js').PathDataItem} PathDataItem

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,5 +1,5 @@
import { collectStylesheet, computeStyle } from '../lib/style.js'; 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'; 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 * @typedef {import('../lib/types.js').Plugin} Plugin

View File

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

View File

@ -1,13 +1,13 @@
import { elemsGroups } from './_collections.js'; import { elemsGroups } from './_collections.js';
import { import {
detachNodeFromParent,
querySelector,
visit, visit,
visitSkip, visitSkip,
querySelector,
detachNodeFromParent,
} from '../lib/xast.js'; } from '../lib/xast.js';
import { collectStylesheet, computeStyle } from '../lib/style.js'; import { collectStylesheet, computeStyle } from '../lib/style.js';
import { parsePathData } from '../lib/path.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 * @typedef {import('../lib/types.js').XastChild} XastChild
@ -183,7 +183,9 @@ export const fn = (root, params) => {
if (node.name === 'use') { if (node.name === 'use') {
for (const attr of Object.keys(node.attributes)) { 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 value = node.attributes[attr];
const id = value.slice(1); const id = value.slice(1);

View File

@ -1,6 +1,6 @@
import { import {
inheritableAttrs,
attrsGroups, attrsGroups,
inheritableAttrs,
presentationNonInheritableGroupAttrs, presentationNonInheritableGroupAttrs,
} from './_collections.js'; } from './_collections.js';
@ -10,7 +10,7 @@ import {
export const name = 'removeNonInheritableGroupAttrs'; export const name = 'removeNonInheritableGroupAttrs';
export const description = export const description =
'removes non-inheritable groups presentational attributes'; "removes non-inheritable group's presentational attributes";
/** /**
* Remove non-inheritable group's "presentation" 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 { parsePathData } from '../lib/path.js';
import { intersects } from './_path.js'; import { intersects } from './_path.js';

View File

@ -1,11 +1,11 @@
import { import {
elems,
attrsGroups, attrsGroups,
elemsGroups,
attrsGroupsDefaults, attrsGroupsDefaults,
elems,
elemsGroups,
presentationNonInheritableGroupAttrs, presentationNonInheritableGroupAttrs,
} from './_collections.js'; } from './_collections.js';
import { visitSkip, detachNodeFromParent } from '../lib/xast.js'; import { detachNodeFromParent, visitSkip } from '../lib/xast.js';
import { collectStylesheet, computeStyle } from '../lib/style.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 { collectStylesheet, computeStyle } from '../lib/style.js';
import { hasScripts } from '../lib/svgo/tools.js'; import { hasScripts } from '../lib/svgo/tools.js';
import { elemsGroups } from './_collections.js'; import { elemsGroups } from './_collections.js';

View File

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

View File

@ -2,7 +2,7 @@ import fs from 'node:fs/promises';
import path from 'path'; import path from 'path';
import { EOL } from 'os'; import { EOL } from 'os';
import { fileURLToPath } from 'url'; 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)); const __dirname = path.dirname(fileURLToPath(import.meta.url));

View File

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