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 | > 2 | @@ -291,11 +301,9 @@ test('provides code snippet in rendered error', () => { 3 | 4 | `); - } }); test('supports errors without path', () => { - expect.assertions(1); const svg = ` @@ -309,11 +317,10 @@ test('supports errors without path', () => { `; - 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