+11
-7
@@ -195,9 +195,9 @@ /** | ||
| const position = args.length; | ||
| if (position === 0 || position === 1) { | ||
| // allow only positive number without sign as first two arguments | ||
| if (c !== '+' && c !== '-') { | ||
| [newCursor, number] = readNumber(string, i); | ||
| } | ||
| } | ||
| if (position === 2 || position === 5 || position === 6) { | ||
| if ( | ||
| position === 0 || | ||
| position === 1 || | ||
| position === 2 || | ||
| position === 5 || | ||
| position === 6 | ||
| ) { | ||
| [newCursor, number] = readNumber(string, i); | ||
@@ -226,2 +226,6 @@ } | ||
| if (args.length === argsCount) { | ||
| if (command === 'A' || command === 'a') { | ||
| args[0] = Math.abs(args[0]); | ||
| args[1] = Math.abs(args[1]); | ||
| } | ||
| pathData.push({ command, args }); | ||
@@ -228,0 +232,0 @@ // subsequent moveto coordinates are treated as implicit lineto commands |
+1
-4
@@ -5,3 +5,3 @@ import fs from 'fs'; | ||
| import { fileURLToPath } from 'url'; | ||
| import { decodeSVGDatauri, encodeSVGDatauri } from './tools.js'; | ||
| import { decodeSVGDatauri } from './tools.js'; | ||
| import { loadConfig, optimize } from '../svgo-node.js'; | ||
@@ -410,5 +410,2 @@ import { builtinPlugins } from '../builtin.js'; | ||
| } | ||
| if (config.datauri) { | ||
| result.data = encodeSVGDatauri(result.data, config.datauri); | ||
| } | ||
| const resultFileSize = Buffer.byteLength(result.data, 'utf8'); | ||
@@ -415,0 +412,0 @@ const processingTime = Date.now() - startTime; |
+13
-1
@@ -173,3 +173,3 @@ import { attrsGroups, referencesProps } from '../../plugins/_collections.js'; | ||
| attrValue != null && | ||
| attrValue.trimStart().startsWith('javascript:'), | ||
| attrValue.trimStart().toLowerCase().startsWith('javascript:'), | ||
| ); | ||
@@ -200,2 +200,14 @@ | ||
| /** | ||
| * @param {string} body | ||
| * @returns {boolean} | ||
| * If body includes syntax that resembles a CSS custom property reference. | ||
| * @example | ||
| * "black" → false | ||
| * "var(--css-custom-property)" → true | ||
| */ | ||
| export const includesCssVarReference = (body) => { | ||
| return /var\s*\(\s*--/.test(body); | ||
| }; | ||
| /** | ||
| * @param {string} attribute | ||
@@ -202,0 +214,0 @@ * @param {string} value |
+2
-1
@@ -16,2 +16,3 @@ import { AddAttributesToSVGElementParams } from '../plugins/addAttributesToSVGElement.js'; | ||
| import { PrefixIdsParams } from '../plugins/prefixIds.js'; | ||
| import { RemoveAttributesBySelectorParams } from '../plugins/removeAttributesBySelector.js'; | ||
| import { RemoveAttrsParams } from '../plugins/removeAttrs.js'; | ||
@@ -103,3 +104,3 @@ import { RemoveCommentsParams } from '../plugins/removeComments.js'; | ||
| addClassesToSVGElement: AddClassesToSVGElementParams; | ||
| removeAttributesBySelector: any; | ||
| removeAttributesBySelector: RemoveAttributesBySelectorParams; | ||
| removeAttrs: RemoveAttrsParams; | ||
@@ -106,0 +107,0 @@ removeElementsByAttr: RemoveElementsByAttrParams; |
+1
-1
@@ -7,2 +7,2 @@ /** | ||
| */ | ||
| export const VERSION = '4.0.1'; | ||
| export const VERSION = '4.0.2'; |
+1
-1
| { | ||
| "packageManager": "yarn@3.8.7", | ||
| "name": "svgo", | ||
| "version": "4.0.1", | ||
| "version": "4.0.2", | ||
| "description": "SVGO is a Node.js library and command-line application for optimizing vector images.", | ||
@@ -6,0 +6,0 @@ "license": "MIT", |
| import { colorsNames, colorsProps, colorsShortNames } from './_collections.js'; | ||
| import { includesUrlReference } from '../lib/svgo/tools.js'; | ||
| import { | ||
| includesCssVarReference, | ||
| includesUrlReference, | ||
| } from '../lib/svgo/tools.js'; | ||
@@ -142,2 +145,3 @@ /** | ||
| !includesUrlReference(val) && | ||
| !includesCssVarReference(val) && | ||
| val !== 'currentColor' | ||
@@ -144,0 +148,0 @@ ) { |
@@ -158,3 +158,2 @@ import { js2path, path2js } from './_path.js'; | ||
| computedStyle['stroke-linecap'].value !== 'butt'); | ||
| const maybeHasStrokeAndLinecap = maybeHasStroke && maybeHasLinecap; | ||
| const isSafeToUseZ = maybeHasStroke | ||
@@ -166,2 +165,15 @@ ? computedStyle['stroke-linecap']?.type === 'static' && | ||
| : true; | ||
| const isSafeToRemove = ( | ||
| /** @type {boolean} */ isFirstDraw, | ||
| /** @type {boolean} */ safeIfNotFirstDraw, | ||
| ) => { | ||
| if (!maybeHasStroke) { | ||
| return true; | ||
| } | ||
| if (isFirstDraw) { | ||
| return !maybeHasLinecap; | ||
| } else { | ||
| return safeIfNotFirstDraw; | ||
| } | ||
| }; | ||
@@ -179,3 +191,3 @@ let data = path2js(node); | ||
| isSafeToUseZ, | ||
| maybeHasStrokeAndLinecap, | ||
| isSafeToRemove, | ||
| hasMarkerMid, | ||
@@ -384,10 +396,6 @@ }); | ||
| * @param {InternalParams} params | ||
| * @param {{ isSafeToUseZ: boolean, maybeHasStrokeAndLinecap: boolean, hasMarkerMid: boolean }} param2 | ||
| * @param {{ isSafeToUseZ: boolean, isSafeToRemove: (isFirstDraw: boolean, safeIfNotFirstDraw: boolean) => boolean, hasMarkerMid: boolean }} param2 | ||
| * @returns {import('../lib/types.js').PathDataItem[]} | ||
| */ | ||
| function filters( | ||
| path, | ||
| params, | ||
| { isSafeToUseZ, maybeHasStrokeAndLinecap, hasMarkerMid }, | ||
| ) { | ||
| function filters(path, params, { isSafeToUseZ, isSafeToRemove, hasMarkerMid }) { | ||
| const stringify = data2Path.bind(null, params); | ||
@@ -669,16 +677,23 @@ const relSubpoint = [0, 0]; | ||
| data = data.slice(-2); | ||
| } else if (command === 'q' && isCurveStraightLine(data)) { | ||
| if (next && next.command == 't') { | ||
| } else if ( | ||
| (command === 'q' && isCurveStraightLine(data)) || | ||
| (command === 't' && prev.command !== 'q' && prev.command !== 't') | ||
| ) { | ||
| if (command == 'q' && next && next.command == 't') { | ||
| makeLonghand(next, data); | ||
| } // fix up next curve | ||
| if (command == 't' && next && next.command == 't') { | ||
| next.command = 'q'; | ||
| next.args.unshift( | ||
| // @ts-expect-error | ||
| // prettier-ignore | ||
| (2 * item.coords[0] - item.base[0]) - item.coords[0], | ||
| // @ts-expect-error | ||
| // prettier-ignore | ||
| (2 * item.coords[1] - item.base[1]) - item.coords[1], | ||
| ); | ||
| } // fix up next curve | ||
| command = 'l'; | ||
| data = data.slice(-2); | ||
| } else if ( | ||
| command === 't' && | ||
| prev.command !== 'q' && | ||
| prev.command !== 't' | ||
| ) { | ||
| command = 'l'; | ||
| data = data.slice(-2); | ||
| } else if ( | ||
| command === 'a' && | ||
@@ -843,3 +858,6 @@ (data[0] === 0 || | ||
| // remove useless non-first path segments | ||
| if (params.removeUseless && !maybeHasStrokeAndLinecap) { | ||
| if ( | ||
| params.removeUseless && | ||
| isSafeToRemove(prev.command == 'm' || prev.command == 'M', true) | ||
| ) { | ||
| // l 0,0 / h 0 / v 0 / q 0,0 0,0 / t 0,0 / c 0,0 0,0 0,0 / s 0,0 0,0 | ||
@@ -900,3 +918,6 @@ if ( | ||
| params.removeUseless && | ||
| isSafeToUseZ && | ||
| isSafeToRemove( | ||
| prev.command == 'm' || prev.command == 'M', | ||
| isSafeToUseZ, | ||
| ) && | ||
| // @ts-expect-error | ||
@@ -903,0 +924,0 @@ Math.abs(item.base[0] - item.coords[0]) < error / 10 && |
@@ -52,4 +52,4 @@ import { collectStylesheet, computeStyle } from '../lib/style.js'; | ||
| /** @type {import('../lib/types.js').XastChild[]} */ | ||
| const elementsToRemove = []; | ||
| /** @type {Set<import('../lib/types.js').XastChild>} */ | ||
| const elementsToRemove = new Set(); | ||
| let prevChild = node.children[0]; | ||
@@ -148,3 +148,3 @@ let prevPathData = null; | ||
| prevPathData.push(...currentPathData); | ||
| elementsToRemove.push(child); | ||
| elementsToRemove.add(child); | ||
| continue; | ||
@@ -166,3 +166,3 @@ } | ||
| node.children = node.children.filter( | ||
| (child) => !elementsToRemove.includes(child), | ||
| (child) => !elementsToRemove.has(child), | ||
| ); | ||
@@ -169,0 +169,0 @@ }, |
| import { querySelectorAll } from '../lib/xast.js'; | ||
| /** | ||
| * @typedef AttributesBySelector | ||
| * @property {string} selector | ||
| * @property {string | string[]} attributes | ||
| * @property {never} selectors | ||
| */ | ||
| /** | ||
| * @typedef AttributesBySelectors | ||
| * @property {never} selector | ||
| * @property {never} attributes | ||
| * @property {Array<AttributesBySelector>} selectors | ||
| */ | ||
| /** | ||
| * @typedef {(AttributesBySelector | AttributesBySelectors)} RemoveAttributesBySelectorParams | ||
| */ | ||
| export const name = 'removeAttributesBySelector'; | ||
@@ -7,2 +25,6 @@ export const description = | ||
| const ENOATTRS = `Warning: The plugin "removeAttributesBySelector" is missing parameters. | ||
| It should have a list of "selectors", or one "selector" and one "attributes". | ||
| Without either, the plugin is a noop.`; | ||
| /** | ||
@@ -17,3 +39,3 @@ * Removes attributes of elements that match a css selector. | ||
| * params: { | ||
| * selector: "[fill='#00ff00']" | ||
| * selector: "[fill='#00ff00']", | ||
| * attributes: "fill" | ||
@@ -72,9 +94,17 @@ * } | ||
| * | ||
| * @link https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors|MDN CSS Selectors | ||
| * @link https://developer.mozilla.org/docs/Web/CSS/CSS_Selectors|MDN CSS Selectors | ||
| * | ||
| * @author Bradley Mease | ||
| * | ||
| * @type {import('../lib/types.js').Plugin<any>} | ||
| * @type {import('../lib/types.js').Plugin<RemoveAttributesBySelectorParams>} | ||
| */ | ||
| export const fn = (root, params) => { | ||
| if ( | ||
| !Array.isArray(params.selectors) && | ||
| (!params.selector || !params.attributes) | ||
| ) { | ||
| console.warn(ENOATTRS); | ||
| return null; | ||
| } | ||
| const selectors = Array.isArray(params.selectors) | ||
@@ -81,0 +111,0 @@ ? params.selectors |
@@ -16,3 +16,34 @@ import { attrsGroups } from './_collections.js'; | ||
| /** Namespaces that support executable <script> elements. */ | ||
| const SCRIPT_NAMESPACES = [ | ||
| 'http://www.w3.org/2000/svg', | ||
| 'http://www.w3.org/1999/xhtml', | ||
| ]; | ||
| /** | ||
| * @param {string} elem | ||
| * @param {string} targetElem | ||
| * @param {ReadonlyMap<string, string[]>} prefixes | ||
| * @param {string[]} targetNamespaces | ||
| * @returns {boolean} | ||
| */ | ||
| function isNamespaceAwareElem(elem, targetElem, prefixes, targetNamespaces) { | ||
| if (elem === targetElem) { | ||
| return true; | ||
| } | ||
| if (elem.includes(':')) { | ||
| const [prefix, effectiveTag] = elem.split(':', 2); | ||
| if (targetElem === effectiveTag) { | ||
| const namespaces = /** @type {string[]} */ (prefixes.get(prefix)); | ||
| const namespace = namespaces[namespaces.length - 1]; | ||
| return targetNamespaces.includes(namespace); | ||
| } | ||
| } | ||
| return false; | ||
| } | ||
| /** | ||
| * Remove scripts. | ||
@@ -26,6 +57,30 @@ * | ||
| export const fn = () => { | ||
| /** | ||
| * Map of XML namespace prefixes to the XML namespace. Each value is a stack | ||
| * as XML namespaces can be pushed to in children elements and revert back | ||
| * previous namespace when we exit that node. | ||
| * | ||
| * @type {Map<string, string[]>} */ | ||
| const prefixes = new Map(); | ||
| return { | ||
| element: { | ||
| enter: (node, parentNode) => { | ||
| if (node.name === 'script') { | ||
| for (const [k, v] of Object.entries(node.attributes)) { | ||
| if (!k.startsWith('xmlns:')) { | ||
| continue; | ||
| } | ||
| const prefix = k.slice(6); | ||
| if (!prefixes.has(prefix)) { | ||
| prefixes.set(prefix, [v]); | ||
| } else { | ||
| /** @type {string[]} */ (prefixes.get(prefix)).push(v); | ||
| } | ||
| } | ||
| if ( | ||
| isNamespaceAwareElem(node.name, 'script', prefixes, SCRIPT_NAMESPACES) | ||
| ) { | ||
| detachNodeFromParent(node, parentNode); | ||
@@ -42,2 +97,11 @@ return; | ||
| exit: (node, parentNode) => { | ||
| for (const k of Object.keys(node.attributes)) { | ||
| if (!k.startsWith('xmlns:')) { | ||
| continue; | ||
| } | ||
| const prefix = k.slice(6); | ||
| /** @type {string[]} */ (prefixes.get(prefix)).pop(); | ||
| } | ||
| if (node.name !== 'a') { | ||
@@ -51,3 +115,6 @@ return; | ||
| node.attributes[attr] == null || | ||
| !node.attributes[attr].trimStart().startsWith('javascript:') | ||
| !node.attributes[attr] | ||
| .trimStart() | ||
| .toLowerCase() | ||
| .startsWith('javascript:') | ||
| ) { | ||
@@ -54,0 +121,0 @@ continue; |
@@ -7,2 +7,3 @@ export function encodeSVGDatauri(str: string, type?: import("../types.js").DataUri | undefined): string; | ||
| export function includesUrlReference(body: string): boolean; | ||
| export function includesCssVarReference(body: string): boolean; | ||
| export function findReferences(attribute: string, value: string): string[]; | ||
@@ -9,0 +10,0 @@ export function toFixed(num: number, precision: number): number; |
@@ -16,2 +16,3 @@ import { AddAttributesToSVGElementParams } from '../plugins/addAttributesToSVGElement.js'; | ||
| import { PrefixIdsParams } from '../plugins/prefixIds.js'; | ||
| import { RemoveAttributesBySelectorParams } from '../plugins/removeAttributesBySelector.js'; | ||
| import { RemoveAttrsParams } from '../plugins/removeAttrs.js'; | ||
@@ -99,3 +100,3 @@ import { RemoveCommentsParams } from '../plugins/removeComments.js'; | ||
| addClassesToSVGElement: AddClassesToSVGElementParams; | ||
| removeAttributesBySelector: any; | ||
| removeAttributesBySelector: RemoveAttributesBySelectorParams; | ||
| removeAttrs: RemoveAttrsParams; | ||
@@ -102,0 +103,0 @@ removeElementsByAttr: RemoveElementsByAttrParams; |
@@ -0,1 +1,16 @@ | ||
| /** | ||
| * @typedef AttributesBySelector | ||
| * @property {string} selector | ||
| * @property {string | string[]} attributes | ||
| * @property {never} selectors | ||
| */ | ||
| /** | ||
| * @typedef AttributesBySelectors | ||
| * @property {never} selector | ||
| * @property {never} attributes | ||
| * @property {Array<AttributesBySelector>} selectors | ||
| */ | ||
| /** | ||
| * @typedef {(AttributesBySelector | AttributesBySelectors)} RemoveAttributesBySelectorParams | ||
| */ | ||
| export const name: "removeAttributesBySelector"; | ||
@@ -12,3 +27,3 @@ export const description: "removes attributes of elements that match a css selector"; | ||
| * params: { | ||
| * selector: "[fill='#00ff00']" | ||
| * selector: "[fill='#00ff00']", | ||
| * attributes: "fill" | ||
@@ -67,8 +82,19 @@ * } | ||
| * | ||
| * @link https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors|MDN CSS Selectors | ||
| * @link https://developer.mozilla.org/docs/Web/CSS/CSS_Selectors|MDN CSS Selectors | ||
| * | ||
| * @author Bradley Mease | ||
| * | ||
| * @type {import('../lib/types.js').Plugin<any>} | ||
| * @type {import('../lib/types.js').Plugin<RemoveAttributesBySelectorParams>} | ||
| */ | ||
| export const fn: import("../lib/types.js").Plugin<any>; | ||
| export const fn: import("../lib/types.js").Plugin<RemoveAttributesBySelectorParams>; | ||
| export type AttributesBySelector = { | ||
| selector: string; | ||
| attributes: string | string[]; | ||
| selectors: never; | ||
| }; | ||
| export type AttributesBySelectors = { | ||
| selector: never; | ||
| attributes: never; | ||
| selectors: Array<AttributesBySelector>; | ||
| }; | ||
| export type RemoveAttributesBySelectorParams = (AttributesBySelector | AttributesBySelectors); |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
1459440
0.55%27738
0.65%