diff --git a/lib/parser.js b/lib/parser.js
index 4569072d..96a37bdd 100644
--- a/lib/parser.js
+++ b/lib/parser.js
@@ -14,15 +14,15 @@
import SAX from 'sax';
import { textElems } from '../plugins/_collections.js';
-class SvgoParserError extends Error {
+export class SvgoParserError extends Error {
/**
- * @param message {string}
- * @param line {number}
- * @param column {number}
- * @param source {string}
- * @param file {void | string}
+ * @param {string} message
+ * @param {number} line
+ * @param {number} column
+ * @param {string} source
+ * @param {string|undefined} file
*/
- constructor(message, line, column, source, file) {
+ constructor(message, line, column, source, file = undefined) {
super(message);
this.name = 'SvgoParserError';
this.message = `${file || ''}:${line}:${column}: ${message}`;
@@ -34,6 +34,7 @@ class SvgoParserError extends Error {
Error.captureStackTrace(this, SvgoParserError);
}
}
+
toString() {
const lines = this.source.split(/\r?\n/);
const startLine = Math.max(this.line - 3, 0);
diff --git a/lib/stringifier.js b/lib/stringifier.js
index 83b8e12f..fbe708c8 100644
--- a/lib/stringifier.js
+++ b/lib/stringifier.js
@@ -67,12 +67,10 @@ const entities = {
/**
* convert XAST to SVG string
*
- * @type {(data: XastRoot, config: StringifyOptions) => string}
+ * @type {(data: XastRoot, userOptions?: StringifyOptions) => string}
*/
export const stringifySvg = (data, userOptions = {}) => {
- /**
- * @type {Options}
- */
+ /** @type {Options} */
const config = { ...defaults, ...userOptions };
const indent = config.indent;
let newIndent = ' ';
@@ -81,9 +79,7 @@ export const stringifySvg = (data, userOptions = {}) => {
} else if (typeof indent === 'string') {
newIndent = indent;
}
- /**
- * @type {State}
- */
+ /** @type {State} */
const state = {
indent: newIndent,
textContext: null,
diff --git a/lib/svgo-node.d.ts b/lib/svgo-node.d.ts
index b2f024d2..06a647d2 100644
--- a/lib/svgo-node.d.ts
+++ b/lib/svgo-node.d.ts
@@ -1,6 +1,6 @@
-import { Config } from './svgo';
+import type { Config } from './svgo.js';
-export * from './svgo';
+export * from './svgo.js';
/**
* If you write a tool on top of svgo you might need a way to load svgo config.
diff --git a/lib/svgo-node.js b/lib/svgo-node.js
index 8b8b31c1..95c9f97c 100644
--- a/lib/svgo-node.js
+++ b/lib/svgo-node.js
@@ -1,6 +1,5 @@
import os from 'os';
import fs from 'fs';
-import { pathToFileURL } from 'url';
import path from 'path';
import {
VERSION,
@@ -11,10 +10,17 @@ import {
_collections,
} from './svgo.js';
+/**
+ * @typedef {import('./svgo.js').Config} Config
+ * @typedef {import('./svgo.js').Output} Output
+ */
+
+/**
+ * @param {string} configFile
+ * @returns {Promise}
+ */
const importConfig = async (configFile) => {
- // dynamic import expects file url instead of path and may fail
- // when windows path is provided
- const imported = await import(pathToFileURL(configFile));
+ const imported = await import(path.resolve(configFile));
const config = imported.default;
if (config == null || typeof config !== 'object' || Array.isArray(config)) {
@@ -23,6 +29,10 @@ const importConfig = async (configFile) => {
return config;
};
+/**
+ * @param {string} file
+ * @returns {Promise}
+ */
const isFile = async (file) => {
try {
const stats = await fs.promises.stat(file);
@@ -40,6 +50,11 @@ export {
_collections,
};
+/**
+ * @param {string} configFile
+ * @param {string} cwd
+ * @returns {Promise}
+ */
export const loadConfig = async (configFile, cwd = process.cwd()) => {
if (configFile != null) {
if (path.isAbsolute(configFile)) {
@@ -71,6 +86,11 @@ export const loadConfig = async (configFile, cwd = process.cwd()) => {
}
};
+/**
+ * @param {string} input
+ * @param {Config} config
+ * @returns {Output}
+ */
export const optimize = (input, config) => {
if (config == null) {
config = {};
diff --git a/lib/svgo-node.test.js b/lib/svgo-node.test.js
index a449a6ef..ebb5f0bb 100644
--- a/lib/svgo-node.test.js
+++ b/lib/svgo-node.test.js
@@ -3,7 +3,7 @@ import path from 'path';
import { optimize, loadConfig } from './svgo-node.js';
/**
- * @typedef {import('../lib/types.js').Plugin} Plugin
+ * @typedef {import('../lib/types.js').Plugin>} Plugin
*/
const describeLF = os.EOL === '\r\n' ? describe.skip : describe;
diff --git a/lib/svgo.d.ts b/lib/svgo.d.ts
index 921f3535..28805d9e 100644
--- a/lib/svgo.d.ts
+++ b/lib/svgo.d.ts
@@ -33,7 +33,7 @@ export type PluginConfig =
}[keyof BuiltinsWithRequiredParams]
| CustomPlugin;
-type BuiltinPlugin = {
+export type BuiltinPlugin = {
/** Name of the plugin, also known as the plugin ID. */
name: Name;
description?: string;
diff --git a/lib/svgo.js b/lib/svgo.js
index be023b7d..3da6afed 100644
--- a/lib/svgo.js
+++ b/lib/svgo.js
@@ -9,6 +9,9 @@ import _collections from '../plugins/_collections.js';
/**
* @typedef {import('./svgo.js').BuiltinPluginOrPreset, ?>} BuiltinPluginOrPreset
+ * @typedef {import('./svgo.js').Config} Config
+ * @typedef {import('./svgo.js').Output} Output
+ * @typedef {import('./svgo.js').PluginConfig} PluginConfig
*/
const pluginsMap = new Map();
@@ -31,6 +34,10 @@ function getPlugin(name) {
return pluginsMap.get(name);
}
+/**
+ * @param {string|PluginConfig} plugin
+ * @returns {?PluginConfig}
+ */
const resolvePluginConfig = (plugin) => {
if (typeof plugin === 'string') {
// resolve builtin plugin specified as string
@@ -49,6 +56,7 @@ const resolvePluginConfig = (plugin) => {
throw Error(`Plugin name must be specified`);
}
// use custom plugin implementation
+ // @ts-expect-error Checking for CustomPlugin with the presence of fn
let fn = plugin.fn;
if (fn == null) {
// resolve builtin plugin implementation
@@ -75,6 +83,11 @@ export {
_collections,
};
+/**
+ * @param {string} input
+ * @param {Config} config
+ * @returns {Output}
+ */
export const optimize = (input, config) => {
if (config == null) {
config = {};
@@ -107,6 +120,8 @@ export const optimize = (input, config) => {
'Warning: plugins list includes null or undefined elements, these will be ignored.',
);
}
+
+ /** @type {Config} */
const globalOverrides = {};
if (config.floatPrecision != null) {
globalOverrides.floatPrecision = config.floatPrecision;
diff --git a/lib/svgo.test.js b/lib/svgo.test.js
index 8c0fbc50..6401ded4 100644
--- a/lib/svgo.test.js
+++ b/lib/svgo.test.js
@@ -1,5 +1,10 @@
import { jest } from '@jest/globals';
import { optimize } from './svgo.js';
+import { SvgoParserError } from './parser.js';
+
+/**
+ * @typedef {import('./svgo.js').CustomPlugin} CustomPlugin
+ */
test('allow to setup default preset', () => {
const svg = `
@@ -65,6 +70,7 @@ test('warn when user tries enable plugins in preset', () => {
const warn = jest.spyOn(console, 'warn');
optimize(svg, {
plugins: [
+ // @ts-expect-error Testing if we receive config that diverges from type definitions.
{
name: 'preset-default',
params: {
@@ -138,6 +144,7 @@ describe('allow to configure EOL', () => {
`;
const { data } = optimize(svg, {
+ // @ts-expect-error Testing if we receive config that diverges from type definitions.
js2svg: { eol: 'invalid', pretty: true, indent: 2 },
});
expect(data).toBe(
@@ -256,34 +263,37 @@ test('plugin precision should override preset precision', () => {
});
test('provides informative error in result', () => {
- expect.assertions(6);
const svg = `
`;
- try {
- optimize(svg, { path: 'test.svg' });
- } catch (error) {
- expect(error.name).toBe('SvgoParserError');
- expect(error.message).toBe('test.svg:2:33: Unquoted attribute value');
- expect(error.reason).toBe('Unquoted attribute value');
- expect(error.line).toBe(2);
- expect(error.column).toBe(33);
- expect(error.source).toBe(svg);
- }
+ const error = new SvgoParserError(
+ 'Unquoted attribute value',
+ 2,
+ 33,
+ svg,
+ 'test.svg',
+ );
+ expect(() => optimize(svg, { path: 'test.svg' })).toThrow(error);
+ expect(error.name).toBe('SvgoParserError');
+ expect(error.message).toBe('test.svg:2:33: Unquoted attribute value');
});
test('provides code snippet in rendered error', () => {
- expect.assertions(1);
const svg = `
`;
- try {
- optimize(svg, { path: 'test.svg' });
- } catch (error) {
- expect(error.toString())
- .toBe(`SvgoParserError: test.svg:2:29: Unquoted attribute value
+ const error = new SvgoParserError(
+ 'Unquoted attribute value',
+ 2,
+ 29,
+ svg,
+ 'test.svg',
+ );
+ expect(() => optimize(svg, { path: 'test.svg' })).toThrow(error);
+ expect(error.toString())
+ .toBe(`SvgoParserError: test.svg:2:29: Unquoted attribute value
1 |
4 |
`);
- }
});
test('supports errors without path', () => {
- expect.assertions(1);
const svg = `
`;
- try {
- optimize(svg);
- } catch (error) {
- expect(error.toString())
- .toBe(`SvgoParserError: :11:29: Unquoted attribute value
+ const error = new SvgoParserError('Unquoted attribute value', 11, 29, svg);
+ expect(() => optimize(svg)).toThrow(error);
+ expect(error.toString())
+ .toBe(`SvgoParserError: :11:29: Unquoted attribute value
9 |
10 |
@@ -322,20 +329,18 @@ test('supports errors without path', () => {
12 |
13 |
`);
- }
});
test('slices long line in error code snippet', () => {
- expect.assertions(1);
const svg = `
`;
- try {
- optimize(svg);
- } catch (error) {
- expect(error.toString())
- .toBe(`SvgoParserError: :2:211: Invalid attribute name
+ const error = new SvgoParserError('Invalid attribute name', 2, 211, svg);
+
+ expect(() => optimize(svg)).toThrow(error);
+ expect(error.toString())
+ .toBe(`SvgoParserError: :2:211: Invalid attribute name
1 | …-0.dtd" viewBox="0 0 230 120">
> 2 | …7.334 250.955 222.534t579.555 155.292z stroke-width="1.5" fill="red" strok…
@@ -343,12 +348,13 @@ test('slices long line in error code snippet', () => {
3 |
4 |
`);
- }
});
test('multipass option should trigger plugins multiple times', () => {
const svg = ``;
+ /** @type {number[]} */
const list = [];
+ /** @type {CustomPlugin} */
const testPlugin = {
name: 'testPlugin',
fn: (_root, _params, info) => {
diff --git a/lib/svgo/coa.js b/lib/svgo/coa.js
index ee11fcde..1aad945e 100644
--- a/lib/svgo/coa.js
+++ b/lib/svgo/coa.js
@@ -5,6 +5,7 @@ import { fileURLToPath } from 'url';
import { encodeSVGDatauri, decodeSVGDatauri } from './tools.js';
import { loadConfig, optimize } from '../svgo-node.js';
import { builtin } from '../builtin.js';
+import { SvgoParserError } from '../parser.js';
/**
* @typedef {import('commander').Command} Command
@@ -14,8 +15,6 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url));
const pkgPath = path.join(__dirname, '../../package.json');
const PKG = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
-const regSVGFile = /\.svg$/i;
-
/**
* Synchronously check if path is a directory. Tolerant to errors like ENOENT.
*
@@ -89,22 +88,17 @@ export default function makeProgram(program) {
.action(action);
}
+/**
+ * @param {string[]} args
+ * @param {any} opts
+ * @param {Command} command
+ * @returns
+ */
async function action(args, opts, command) {
- var input = opts.input || args;
- var output = opts.output;
- var config = {};
-
- if (opts.precision != null) {
- const number = Number.parseInt(opts.precision, 10);
- if (Number.isNaN(number)) {
- console.error(
- "error: option '-p, --precision' argument must be an integer number",
- );
- process.exit(1);
- } else {
- opts.precision = number;
- }
- }
+ const input = opts.input || args;
+ let output = opts.output;
+ /** @type {any} */
+ let config = {};
if (opts.datauri != null) {
if (
@@ -155,13 +149,8 @@ async function action(args, opts, command) {
return command.help();
}
- if (
- typeof process == 'object' &&
- process.versions &&
- process.versions.node &&
- PKG &&
- PKG.engines.node
- ) {
+ 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];
if (parseFloat(process.versions.node) < parseFloat(nodeVersion)) {
throw Error(
@@ -188,13 +177,20 @@ async function action(args, opts, command) {
// --exclude
config.exclude = opts.exclude
- ? opts.exclude.map((pattern) => RegExp(pattern))
+ ? opts.exclude.map((/** @type {string} */ pattern) => RegExp(pattern))
: [];
// --precision
if (opts.precision != null) {
- var precision = Math.min(Math.max(0, opts.precision), 20);
- config.floatPrecision = precision;
+ const number = Number.parseInt(opts.precision, 10);
+ if (Number.isNaN(number)) {
+ console.error(
+ "error: option '-p, --precision' argument must be an integer number",
+ );
+ process.exit(1);
+ } else {
+ config.floatPrecision = Math.min(Math.max(0, number), 20);
+ }
}
// --multipass
@@ -227,7 +223,7 @@ async function action(args, opts, command) {
if (output) {
if (input.length && input[0] != '-') {
if (output.length == 1 && checkIsDir(output[0])) {
- var dir = output[0];
+ const dir = output[0];
for (var i = 0; i < input.length; i++) {
output[i] = checkIsDir(input[i])
? input[i]
@@ -270,7 +266,9 @@ async function action(args, opts, command) {
// file
} else {
await Promise.all(
- input.map((file, n) => optimizeFile(config, file, output[n])),
+ input.map((/** @type {string} */ file, /** @type {number} */ n) =>
+ optimizeFile(config, file, output[n]),
+ ),
);
}
@@ -285,10 +283,10 @@ async function action(args, opts, command) {
/**
* Optimize SVG files in a directory.
*
- * @param {Object} config options
+ * @param {any} config options
* @param {string} dir input directory
* @param {string} output output directory
- * @return {Promise}
+ * @return {Promise}
*/
function optimizeFolder(config, dir, output) {
if (!config.quiet) {
@@ -302,11 +300,11 @@ function optimizeFolder(config, dir, output) {
/**
* Process given files, take only SVG.
*
- * @param {Object} config options
+ * @param {any} config options
* @param {string} dir input directory
- * @param {Array} files list of file names in the directory
+ * @param {string[]} files list of file names in the directory
* @param {string} output output directory
- * @return {Promise}
+ * @return {Promise}
*/
function processDirectory(config, dir, files, output) {
// take only *.svg files, recursively if necessary
@@ -330,43 +328,46 @@ function processDirectory(config, dir, files, output) {
/**
* Get SVG files descriptions.
*
- * @param {Object} config options
+ * @param {any} config options
* @param {string} dir input directory
- * @param {Array} files list of file names in the directory
+ * @param {string[]} files list of file names in the directory
* @param {string} output output directory
- * @return {Array}
+ * @return {any[]}
*/
function getFilesDescriptions(config, dir, files, output) {
const filesInThisFolder = files
.filter(
(name) =>
- regSVGFile.test(name) &&
- !config.exclude.some((regExclude) => regExclude.test(name)),
+ name.slice(-4).toLowerCase() === '.svg' &&
+ !config.exclude.some((/** @type {RegExp} */ regExclude) =>
+ regExclude.test(name),
+ ),
)
.map((name) => ({
inputPath: path.resolve(dir, name),
outputPath: path.resolve(output, name),
}));
- return config.recursive
- ? [].concat(
- filesInThisFolder,
- files
- .filter((name) => checkIsDir(path.resolve(dir, name)))
- .map((subFolderName) => {
- const subFolderPath = path.resolve(dir, subFolderName);
- const subFolderFiles = fs.readdirSync(subFolderPath);
- const subFolderOutput = path.resolve(output, subFolderName);
- return getFilesDescriptions(
- config,
- subFolderPath,
- subFolderFiles,
- subFolderOutput,
- );
- })
- .reduce((a, b) => [].concat(a, b), []),
- )
- : filesInThisFolder;
+ if (!config.recursive) {
+ return filesInThisFolder;
+ }
+
+ return filesInThisFolder.concat(
+ files
+ .filter((name) => checkIsDir(path.resolve(dir, name)))
+ .map((subFolderName) => {
+ const subFolderPath = path.resolve(dir, subFolderName);
+ const subFolderFiles = fs.readdirSync(subFolderPath);
+ const subFolderOutput = path.resolve(output, subFolderName);
+ return getFilesDescriptions(
+ config,
+ subFolderPath,
+ subFolderFiles,
+ subFolderOutput,
+ );
+ })
+ .reduce((a, b) => a.concat(b), []),
+ );
}
/**
@@ -375,7 +376,7 @@ function getFilesDescriptions(config, dir, files, output) {
* @param {Object} config options
* @param {string} file
* @param {string} output
- * @return {Promise}
+ * @return {Promise}
*/
function optimizeFile(config, file, output) {
return fs.promises.readFile(file, 'utf8').then(
@@ -387,13 +388,14 @@ function optimizeFile(config, file, output) {
/**
* Optimize SVG data.
*
- * @param {Object} config options
+ * @param {any} config options
+ * @param {?{ path: string }} info
* @param {string} data SVG content to optimize
* @param {string} output where to write optimized file
- * @param {string} [input] input file name (being used if output is a directory)
- * @return {Promise}
+ * @param {any} input input file name (being used if output is a directory)
+ * @return {Promise}
*/
-function processSVGData(config, info, data, output, input) {
+function processSVGData(config, info, data, output, input = undefined) {
var startTime = Date.now(),
prevFileSize = Buffer.byteLength(data, 'utf8');
@@ -401,7 +403,7 @@ function processSVGData(config, info, data, output, input) {
try {
result = optimize(data, { ...config, ...info });
} catch (error) {
- if (error.name === 'SvgoParserError') {
+ if (error instanceof SvgoParserError) {
console.error(colors.red(error.toString()));
process.exit(1);
} else {
@@ -441,7 +443,7 @@ function processSVGData(config, info, data, output, input) {
* @param {string} input
* @param {string} output output file name. '-' for stdout
* @param {string} data data to write
- * @return {Promise}
+ * @return {Promise}
*/
function writeOutput(input, output, data) {
if (output == '-') {
@@ -488,11 +490,11 @@ function printProfitInfo(inBytes, outBytes) {
/**
* Check for errors, if it's a dir optimize the dir.
*
- * @param {Object} config
+ * @param {any} config
* @param {string} input
* @param {string} output
- * @param {Error} error
- * @return {Promise}
+ * @param {Error & { code: string, path: string }} error
+ * @return {Promise}
*/
function checkOptimizeFileError(config, input, output, error) {
if (error.code == 'EISDIR') {
@@ -511,8 +513,8 @@ function checkOptimizeFileError(config, input, output, error) {
* @param {string} input
* @param {string} output
* @param {string} data
- * @param {Error} error
- * @return {Promise}
+ * @param {Error & { code: string }} error
+ * @return {Promise}
*/
function checkWriteFileError(input, output, data, error) {
if (error.code == 'EISDIR' && input) {
diff --git a/lib/svgo/css-select-adapter.js b/lib/svgo/css-select-adapter.js
index 450c512a..a511ae61 100644
--- a/lib/svgo/css-select-adapter.js
+++ b/lib/svgo/css-select-adapter.js
@@ -1,49 +1,95 @@
+/**
+ * @typedef {import('../types.js').XastChild} XastChild
+ * @typedef {import('../types.js').XastElement} XastElement
+ * @typedef {import('../types.js').XastNode} XastNode
+ * @typedef {import('../types.js').XastParent} XastParent
+ */
+
+/**
+ * @param {XastNode} node
+ * @returns {boolean}
+ */
const isTag = (node) => {
return node.type === 'element';
};
+/**
+ * @param {(v: T) => boolean} test
+ * @param {XastNode[]} elems
+ * @returns {boolean}
+ */
const existsOne = (test, elems) => {
return elems.some((elem) => {
- if (isTag(elem)) {
- return test(elem) || existsOne(test, getChildren(elem));
- } else {
- return false;
- }
+ return isTag(elem) && (test(elem) || existsOne(test, getChildren(elem)));
});
};
+/**
+ * @param {XastElement} elem
+ * @param {string} name
+ * @returns {?string}
+ */
const getAttributeValue = (elem, name) => {
return elem.attributes[name];
};
+/**
+ * @param {XastNode & { children?: XastChild[] }} node
+ * @returns {XastChild[]}
+ */
const getChildren = (node) => {
return node.children || [];
};
+/**
+ * @param {XastElement} elemAst
+ * @returns {string}
+ */
const getName = (elemAst) => {
return elemAst.name;
};
+/**
+ * @param {XastNode & { parentNode?: XastParent }} node
+ * @returns {?XastParent}
+ */
const getParent = (node) => {
return node.parentNode || null;
};
+/**
+ * @param {XastElement} elem
+ * @returns {XastChild[]}
+ */
const getSiblings = (elem) => {
- var parent = getParent(elem);
+ const parent = getParent(elem);
return parent ? getChildren(parent) : [];
};
+/**
+ * @param {XastParent} node
+ * @returns {string}
+ */
const getText = (node) => {
- if (node.children[0].type === 'text' && node.children[0].type === 'cdata') {
+ if (node.children[0].type === 'text' || node.children[0].type === 'cdata') {
return node.children[0].value;
}
return '';
};
+/**
+ * @param {XastElement} elem
+ * @param {string} name
+ * @returns {boolean}
+ */
const hasAttrib = (elem, name) => {
return elem.attributes[name] !== undefined;
};
+/**
+ * @param {Array} nodes
+ * @returns {Array}
+ */
const removeSubsets = (nodes) => {
let idx = nodes.length;
let node;
@@ -72,6 +118,11 @@ const removeSubsets = (nodes) => {
return nodes;
};
+/**
+ * @param {(v: T) => boolean} test
+ * @param {XastNode[]} elems
+ * @returns {XastNode[]}
+ */
const findAll = (test, elems) => {
const result = [];
for (const elem of elems) {
@@ -85,6 +136,11 @@ const findAll = (test, elems) => {
return result;
};
+/**
+ * @param {(v: T) => boolean} test
+ * @param {XastNode[]} elems
+ * @returns {?XastNode}
+ */
const findOne = (test, elems) => {
for (const elem of elems) {
if (isTag(elem)) {
@@ -100,7 +156,7 @@ const findOne = (test, elems) => {
return null;
};
-const svgoCssSelectAdapter = {
+export default {
isTag,
existsOne,
getAttributeValue,
@@ -114,5 +170,3 @@ const svgoCssSelectAdapter = {
findAll,
findOne,
};
-
-export default svgoCssSelectAdapter;
diff --git a/lib/svgo/plugins.js b/lib/svgo/plugins.js
index 5158c972..438fbd47 100644
--- a/lib/svgo/plugins.js
+++ b/lib/svgo/plugins.js
@@ -1,8 +1,9 @@
import { visit } from '../xast.js';
/**
- * @typedef {import('../svgo').BuiltinPlugin} BuiltinPlugin
- * @typedef {import('../svgo').BuiltinPluginOrPreset, ?>} BuiltinPreset
+ * @typedef {import('../svgo.js').BuiltinPlugin} BuiltinPlugin
+ * @typedef {import('../svgo.js').BuiltinPluginOrPreset, ?>} BuiltinPreset
+ * @typedef {import('../types.js').XastNode} XastNode
*/
/**
@@ -10,10 +11,11 @@ import { visit } from '../xast.js';
*
* @module plugins
*
- * @param {Object} ast input ast
- * @param {Object} info extra information
- * @param {Array} plugins plugins object from config
- * @return {Object} output ast
+ * @param {XastNode} ast Input AST.
+ * @param {Object} info Extra information.
+ * @param {Array} plugins Plugins property from config.
+ * @param {any} overrides
+ * @param {any} globalOverrides
*/
export const invokePlugins = (
ast,
diff --git a/package.json b/package.json
index d6667dfd..934343b1 100644
--- a/package.json
+++ b/package.json
@@ -116,6 +116,7 @@
},
"devDependencies": {
"@eslint/js": "^9.3.0",
+ "@jest/globals": "^29.7.0",
"@rollup/plugin-commonjs": "^25.0.7",
"@rollup/plugin-node-resolve": "^15.2.3",
"@rollup/plugin-terser": "^0.4.4",
@@ -135,7 +136,7 @@
"rimraf": "^5.0.7",
"rollup": "^4.17.2",
"tar-stream": "^3.1.7",
- "typescript": "^5.4.5"
+ "typescript": "^5.8.3"
},
"resolutions": {
"sax@^1.4.1": "patch:sax@npm%3A1.4.1#./.yarn/patches/sax-npm-1.4.1-503b1923cb.patch"
diff --git a/plugins/cleanupListOfValues.js b/plugins/cleanupListOfValues.js
index 5bf8fa0e..5c2e6661 100644
--- a/plugins/cleanupListOfValues.js
+++ b/plugins/cleanupListOfValues.js
@@ -54,9 +54,7 @@ export const fn = (_root, params) => {
if (match) {
// round it to the fixed precision
let num = Number(Number(match[1]).toFixed(floatPrecision));
- /**
- * @type {any}
- */
+ /** @type {any} */
let matchedUnit = match[3] || '';
/**
* @type{'' | keyof typeof absoluteLengths}
diff --git a/plugins/cleanupNumericValues.js b/plugins/cleanupNumericValues.js
index 9eed0ab2..4c1d7e18 100644
--- a/plugins/cleanupNumericValues.js
+++ b/plugins/cleanupNumericValues.js
@@ -60,13 +60,9 @@ export const fn = (_root, params) => {
if (match) {
// round it to the fixed precision
let num = Number(Number(match[1]).toFixed(floatPrecision));
- /**
- * @type {any}
- */
+ /** @type {any} */
let matchedUnit = match[3] || '';
- /**
- * @type{'' | keyof typeof absoluteLengths}
- */
+ /** @type {'' | keyof typeof absoluteLengths} */
let units = matchedUnit;
// convert absolute values to pixels
diff --git a/plugins/inlineStyles.js b/plugins/inlineStyles.js
index d9814387..67841b7e 100644
--- a/plugins/inlineStyles.js
+++ b/plugins/inlineStyles.js
@@ -78,7 +78,6 @@ export const fn = (root, params) => {
const cssText = node.children
.filter((child) => child.type === 'text' || child.type === 'cdata')
- // @ts-expect-error
.map((child) => child.value)
.join('');
@@ -368,7 +367,6 @@ export const fn = (root, params) => {
},
});
- // csstree v2 changed this type
if (style.cssAst.children.isEmpty) {
// remove empty style element
detachNodeFromParent(style.node, style.parentNode);
diff --git a/tsconfig.json b/tsconfig.json
index a98c3a04..4dd2ac5c 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -9,15 +9,5 @@
"strict": true,
"resolveJsonModule": true
},
- "include": ["lib/**/*.js", "plugins/**/*", "test/cli/**/*"],
- "exclude": [
- "lib/svgo-node.js",
- "lib/svgo-node.test.js",
- "lib/svgo.js",
- "lib/builtin.js",
- "lib/svgo.test.js",
- "lib/svgo/**/*.js",
- "plugins/plugins.js",
- "plugins/preset-default.js"
- ]
+ "include": ["lib/**/*.js", "plugins/**/*", "test/cli/**/*"]
}
diff --git a/yarn.lock b/yarn.lock
index 7b9bde29..d11c1797 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -4491,6 +4491,7 @@ __metadata:
resolution: "svgo@workspace:."
dependencies:
"@eslint/js": ^9.3.0
+ "@jest/globals": ^29.7.0
"@rollup/plugin-commonjs": ^25.0.7
"@rollup/plugin-node-resolve": ^15.2.3
"@rollup/plugin-terser": ^0.4.4
@@ -4517,7 +4518,7 @@ __metadata:
rollup: ^4.17.2
sax: ^1.4.1
tar-stream: ^3.1.7
- typescript: ^5.4.5
+ typescript: ^5.8.3
bin:
svgo: ./bin/svgo.js
languageName: unknown
@@ -4626,23 +4627,23 @@ __metadata:
languageName: node
linkType: hard
-"typescript@npm:^5.4.5":
- version: 5.4.5
- resolution: "typescript@npm:5.4.5"
+"typescript@npm:^5.8.3":
+ version: 5.8.3
+ resolution: "typescript@npm:5.8.3"
bin:
tsc: bin/tsc
tsserver: bin/tsserver
- checksum: 53c879c6fa1e3bcb194b274d4501ba1985894b2c2692fa079db03c5a5a7140587a1e04e1ba03184605d35f439b40192d9e138eb3279ca8eee313c081c8bcd9b0
+ checksum: cb1d081c889a288b962d3c8ae18d337ad6ee88a8e81ae0103fa1fecbe923737f3ba1dbdb3e6d8b776c72bc73bfa6d8d850c0306eed1a51377d2fccdfd75d92c4
languageName: node
linkType: hard
-"typescript@patch:typescript@^5.4.5#~builtin":
- version: 5.4.5
- resolution: "typescript@patch:typescript@npm%3A5.4.5#~builtin::version=5.4.5&hash=5adc0c"
+"typescript@patch:typescript@^5.8.3#~builtin":
+ version: 5.8.3
+ resolution: "typescript@patch:typescript@npm%3A5.8.3#~builtin::version=5.8.3&hash=5786d5"
bin:
tsc: bin/tsc
tsserver: bin/tsserver
- checksum: d59e26e74f6b444517d0ba16e0ee16e75c652c2b49a59f2ebdbeb16647a855fd50c7fc786a58987e45f03bce0677092e2dd3333649fd53b11d0b0d271455837c
+ checksum: f1743b6850976b3debf7cbd53d2bc0b67e75d47eb6410db564c8bb475e92a8a48f8a3fcd14d89cf93426835281c31a8f8a94dad90be4dc899279a898532ba97f
languageName: node
linkType: hard