diff options
| author | littleblack111 <littleblack11111@gmail.com> | 2026-05-18 23:10:59 +0800 |
|---|---|---|
| committer | littleblack111 <littleblack11111@gmail.com> | 2026-05-18 23:41:44 +0800 |
| commit | fc87c009806dcc98867f6b58efe29b042aa01dee (patch) | |
| tree | 8f59b3f4a5b66e9dfef2d40c8d318e258a521228 /node_modules/svgo/plugins | |
| parent | 3336b2356ee6152d9c6ae59bd9b87ccbc0958407 (diff) | |
node: updatemain
Diffstat (limited to 'node_modules/svgo/plugins')
58 files changed, 0 insertions, 11613 deletions
diff --git a/node_modules/svgo/plugins/_collections.js b/node_modules/svgo/plugins/_collections.js deleted file mode 100644 index 6f11b136..00000000 --- a/node_modules/svgo/plugins/_collections.js +++ /dev/null @@ -1,2339 +0,0 @@ -'use strict'; - -// https://www.w3.org/TR/SVG11/intro.html#Definitions - -/** - * @type {Record<string, Set<string>>} - */ -exports.elemsGroups = { - animation: new Set([ - 'animate', - 'animateColor', - 'animateMotion', - 'animateTransform', - 'set', - ]), - descriptive: new Set(['desc', 'metadata', 'title']), - shape: new Set([ - 'circle', - 'ellipse', - 'line', - 'path', - 'polygon', - 'polyline', - 'rect', - ]), - structural: new Set(['defs', 'g', 'svg', 'symbol', 'use']), - paintServer: new Set([ - 'hatch', - 'linearGradient', - 'meshGradient', - 'pattern', - 'radialGradient', - 'solidColor', - ]), - nonRendering: new Set([ - 'clipPath', - 'filter', - 'linearGradient', - 'marker', - 'mask', - 'pattern', - 'radialGradient', - 'solidColor', - 'symbol', - ]), - container: new Set([ - 'a', - 'defs', - 'foreignObject', - 'g', - 'marker', - 'mask', - 'missing-glyph', - 'pattern', - 'svg', - 'switch', - 'symbol', - ]), - textContent: new Set([ - 'altGlyph', - 'altGlyphDef', - 'altGlyphItem', - 'glyph', - 'glyphRef', - 'text', - 'textPath', - 'tref', - 'tspan', - ]), - textContentChild: new Set(['altGlyph', 'textPath', 'tref', 'tspan']), - lightSource: new Set([ - 'feDiffuseLighting', - 'feDistantLight', - 'fePointLight', - 'feSpecularLighting', - 'feSpotLight', - ]), - filterPrimitive: new Set([ - 'feBlend', - 'feColorMatrix', - 'feComponentTransfer', - 'feComposite', - 'feConvolveMatrix', - 'feDiffuseLighting', - 'feDisplacementMap', - 'feDropShadow', - 'feFlood', - 'feFuncA', - 'feFuncB', - 'feFuncG', - 'feFuncR', - 'feGaussianBlur', - 'feImage', - 'feMerge', - 'feMergeNode', - 'feMorphology', - 'feOffset', - 'feSpecularLighting', - 'feTile', - 'feTurbulence', - ]), -}; - -/** - * Elements where adding or removing whitespace may effect rendering, metadata, - * or semantic meaning. - * - * @see https://developer.mozilla.org/docs/Web/HTML/Element/pre - */ -exports.textElems = new Set([ - ...exports.elemsGroups.textContent, - 'pre', - 'title', -]); - -exports.pathElems = new Set(['glyph', 'missing-glyph', 'path']); - -/** - * @type {Record<string, Set<string>>} - * @see https://www.w3.org/TR/SVG11/intro.html#Definitions - */ -exports.attrsGroups = { - animationAddition: new Set(['additive', 'accumulate']), - animationAttributeTarget: new Set(['attributeType', 'attributeName']), - animationEvent: new Set(['onbegin', 'onend', 'onrepeat', 'onload']), - animationTiming: new Set([ - 'begin', - 'dur', - 'end', - 'fill', - 'max', - 'min', - 'repeatCount', - 'repeatDur', - 'restart', - ]), - animationValue: new Set([ - 'by', - 'calcMode', - 'from', - 'keySplines', - 'keyTimes', - 'to', - 'values', - ]), - conditionalProcessing: new Set([ - 'requiredExtensions', - 'requiredFeatures', - 'systemLanguage', - ]), - core: new Set(['id', 'tabindex', 'xml:base', 'xml:lang', 'xml:space']), - graphicalEvent: new Set([ - 'onactivate', - 'onclick', - 'onfocusin', - 'onfocusout', - 'onload', - 'onmousedown', - 'onmousemove', - 'onmouseout', - 'onmouseover', - 'onmouseup', - ]), - presentation: new Set([ - 'alignment-baseline', - 'baseline-shift', - 'clip-path', - 'clip-rule', - 'clip', - 'color-interpolation-filters', - 'color-interpolation', - 'color-profile', - 'color-rendering', - 'color', - 'cursor', - 'direction', - 'display', - 'dominant-baseline', - 'enable-background', - 'fill-opacity', - 'fill-rule', - 'fill', - 'filter', - 'flood-color', - 'flood-opacity', - 'font-family', - 'font-size-adjust', - 'font-size', - 'font-stretch', - 'font-style', - 'font-variant', - 'font-weight', - 'glyph-orientation-horizontal', - 'glyph-orientation-vertical', - 'image-rendering', - 'letter-spacing', - 'lighting-color', - 'marker-end', - 'marker-mid', - 'marker-start', - 'mask', - 'opacity', - 'overflow', - 'paint-order', - 'pointer-events', - 'shape-rendering', - 'stop-color', - 'stop-opacity', - 'stroke-dasharray', - 'stroke-dashoffset', - 'stroke-linecap', - 'stroke-linejoin', - 'stroke-miterlimit', - 'stroke-opacity', - 'stroke-width', - 'stroke', - 'text-anchor', - 'text-decoration', - 'text-overflow', - 'text-rendering', - 'transform-origin', - 'transform', - 'unicode-bidi', - 'vector-effect', - 'visibility', - 'word-spacing', - 'writing-mode', - ]), - xlink: new Set([ - 'xlink:actuate', - 'xlink:arcrole', - 'xlink:href', - 'xlink:role', - 'xlink:show', - 'xlink:title', - 'xlink:type', - ]), - documentEvent: new Set([ - 'onabort', - 'onerror', - 'onresize', - 'onscroll', - 'onunload', - 'onzoom', - ]), - documentElementEvent: new Set(['oncopy', 'oncut', 'onpaste']), - globalEvent: new Set([ - 'oncancel', - 'oncanplay', - 'oncanplaythrough', - 'onchange', - 'onclick', - 'onclose', - 'oncuechange', - 'ondblclick', - 'ondrag', - 'ondragend', - 'ondragenter', - 'ondragleave', - 'ondragover', - 'ondragstart', - 'ondrop', - 'ondurationchange', - 'onemptied', - 'onended', - 'onerror', - 'onfocus', - 'oninput', - 'oninvalid', - 'onkeydown', - 'onkeypress', - 'onkeyup', - 'onload', - 'onloadeddata', - 'onloadedmetadata', - 'onloadstart', - 'onmousedown', - 'onmouseenter', - 'onmouseleave', - 'onmousemove', - 'onmouseout', - 'onmouseover', - 'onmouseup', - 'onmousewheel', - 'onpause', - 'onplay', - 'onplaying', - 'onprogress', - 'onratechange', - 'onreset', - 'onresize', - 'onscroll', - 'onseeked', - 'onseeking', - 'onselect', - 'onshow', - 'onstalled', - 'onsubmit', - 'onsuspend', - 'ontimeupdate', - 'ontoggle', - 'onvolumechange', - 'onwaiting', - ]), - filterPrimitive: new Set(['x', 'y', 'width', 'height', 'result']), - transferFunction: new Set([ - 'amplitude', - 'exponent', - 'intercept', - 'offset', - 'slope', - 'tableValues', - 'type', - ]), -}; - -/** - * @type {Record<string, Record<string, string>>} - */ -exports.attrsGroupsDefaults = { - core: { 'xml:space': 'default' }, - presentation: { - clip: 'auto', - 'clip-path': 'none', - 'clip-rule': 'nonzero', - mask: 'none', - opacity: '1', - 'stop-color': '#000', - 'stop-opacity': '1', - 'fill-opacity': '1', - 'fill-rule': 'nonzero', - fill: '#000', - stroke: 'none', - 'stroke-width': '1', - 'stroke-linecap': 'butt', - 'stroke-linejoin': 'miter', - 'stroke-miterlimit': '4', - 'stroke-dasharray': 'none', - 'stroke-dashoffset': '0', - 'stroke-opacity': '1', - 'paint-order': 'normal', - 'vector-effect': 'none', - display: 'inline', - visibility: 'visible', - 'marker-start': 'none', - 'marker-mid': 'none', - 'marker-end': 'none', - 'color-interpolation': 'sRGB', - 'color-interpolation-filters': 'linearRGB', - 'color-rendering': 'auto', - 'shape-rendering': 'auto', - 'text-rendering': 'auto', - 'image-rendering': 'auto', - 'font-style': 'normal', - 'font-variant': 'normal', - 'font-weight': 'normal', - 'font-stretch': 'normal', - 'font-size': 'medium', - 'font-size-adjust': 'none', - kerning: 'auto', - 'letter-spacing': 'normal', - 'word-spacing': 'normal', - 'text-decoration': 'none', - 'text-anchor': 'start', - 'text-overflow': 'clip', - 'writing-mode': 'lr-tb', - 'glyph-orientation-vertical': 'auto', - 'glyph-orientation-horizontal': '0deg', - direction: 'ltr', - 'unicode-bidi': 'normal', - 'dominant-baseline': 'auto', - 'alignment-baseline': 'baseline', - 'baseline-shift': 'baseline', - }, - transferFunction: { - slope: '1', - intercept: '0', - amplitude: '1', - exponent: '1', - offset: '0', - }, -}; - -/** - * @type {Record<string, { - * attrsGroups: Set<string>, - * attrs?: Set<string>, - * defaults?: Record<string, string>, - * contentGroups?: Set<string>, - * content?: Set<string>, - * }>} - * @see https://www.w3.org/TR/SVG11/eltindex.html - */ -exports.elems = { - a: { - attrsGroups: new Set([ - 'conditionalProcessing', - 'core', - 'graphicalEvent', - 'presentation', - 'xlink', - ]), - attrs: new Set([ - 'class', - 'externalResourcesRequired', - 'style', - 'target', - 'transform', - ]), - defaults: { - target: '_self', - }, - contentGroups: new Set([ - 'animation', - 'descriptive', - 'paintServer', - 'shape', - 'structural', - ]), - content: new Set([ - 'a', - 'altGlyphDef', - 'clipPath', - 'color-profile', - 'cursor', - 'filter', - 'font-face', - 'font', - 'foreignObject', - 'image', - 'marker', - 'mask', - 'pattern', - 'script', - 'style', - 'switch', - 'text', - 'view', - // not spec compliant - 'tspan', - ]), - }, - altGlyph: { - attrsGroups: new Set([ - 'conditionalProcessing', - 'core', - 'graphicalEvent', - 'presentation', - 'xlink', - ]), - attrs: new Set([ - 'class', - 'dx', - 'dy', - 'externalResourcesRequired', - 'format', - 'glyphRef', - 'rotate', - 'style', - 'x', - 'y', - ]), - }, - altGlyphDef: { - attrsGroups: new Set(['core']), - content: new Set(['glyphRef']), - }, - altGlyphItem: { - attrsGroups: new Set(['core']), - content: new Set(['glyphRef', 'altGlyphItem']), - }, - animate: { - attrsGroups: new Set([ - 'animationAddition', - 'animationAttributeTarget', - 'animationEvent', - 'animationTiming', - 'animationValue', - 'conditionalProcessing', - 'core', - 'presentation', - 'xlink', - ]), - attrs: new Set(['externalResourcesRequired']), - contentGroups: new Set(['descriptive']), - }, - animateColor: { - attrsGroups: new Set([ - 'animationAddition', - 'animationAttributeTarget', - 'animationEvent', - 'animationTiming', - 'animationValue', - 'conditionalProcessing', - 'core', - 'presentation', - 'xlink', - ]), - attrs: new Set(['externalResourcesRequired']), - contentGroups: new Set(['descriptive']), - }, - animateMotion: { - attrsGroups: new Set([ - 'animationAddition', - 'animationEvent', - 'animationTiming', - 'animationValue', - 'conditionalProcessing', - 'core', - 'xlink', - ]), - attrs: new Set([ - 'externalResourcesRequired', - 'keyPoints', - 'origin', - 'path', - 'rotate', - ]), - defaults: { - rotate: '0', - }, - contentGroups: new Set(['descriptive']), - content: new Set(['mpath']), - }, - animateTransform: { - attrsGroups: new Set([ - 'animationAddition', - 'animationAttributeTarget', - 'animationEvent', - 'animationTiming', - 'animationValue', - 'conditionalProcessing', - 'core', - 'xlink', - ]), - attrs: new Set(['externalResourcesRequired', 'type']), - contentGroups: new Set(['descriptive']), - }, - circle: { - attrsGroups: new Set([ - 'conditionalProcessing', - 'core', - 'graphicalEvent', - 'presentation', - ]), - attrs: new Set([ - 'class', - 'cx', - 'cy', - 'externalResourcesRequired', - 'r', - 'style', - 'transform', - ]), - defaults: { - cx: '0', - cy: '0', - }, - contentGroups: new Set(['animation', 'descriptive']), - }, - clipPath: { - attrsGroups: new Set(['conditionalProcessing', 'core', 'presentation']), - attrs: new Set([ - 'class', - 'clipPathUnits', - 'externalResourcesRequired', - 'style', - 'transform', - ]), - defaults: { - clipPathUnits: 'userSpaceOnUse', - }, - contentGroups: new Set(['animation', 'descriptive', 'shape']), - content: new Set(['text', 'use']), - }, - 'color-profile': { - attrsGroups: new Set(['core', 'xlink']), - attrs: new Set(['local', 'name', 'rendering-intent']), - defaults: { - name: 'sRGB', - 'rendering-intent': 'auto', - }, - contentGroups: new Set(['descriptive']), - }, - cursor: { - attrsGroups: new Set(['core', 'conditionalProcessing', 'xlink']), - attrs: new Set(['externalResourcesRequired', 'x', 'y']), - defaults: { - x: '0', - y: '0', - }, - contentGroups: new Set(['descriptive']), - }, - defs: { - attrsGroups: new Set([ - 'conditionalProcessing', - 'core', - 'graphicalEvent', - 'presentation', - ]), - attrs: new Set([ - 'class', - 'externalResourcesRequired', - 'style', - 'transform', - ]), - contentGroups: new Set([ - 'animation', - 'descriptive', - 'paintServer', - 'shape', - 'structural', - ]), - content: new Set([ - 'a', - 'altGlyphDef', - 'clipPath', - 'color-profile', - 'cursor', - 'filter', - 'font-face', - 'font', - 'foreignObject', - 'image', - 'marker', - 'mask', - 'pattern', - 'script', - 'style', - 'switch', - 'text', - 'view', - ]), - }, - desc: { - attrsGroups: new Set(['core']), - attrs: new Set(['class', 'style']), - }, - ellipse: { - attrsGroups: new Set([ - 'conditionalProcessing', - 'core', - 'graphicalEvent', - 'presentation', - ]), - attrs: new Set([ - 'class', - 'cx', - 'cy', - 'externalResourcesRequired', - 'rx', - 'ry', - 'style', - 'transform', - ]), - defaults: { - cx: '0', - cy: '0', - }, - contentGroups: new Set(['animation', 'descriptive']), - }, - feBlend: { - attrsGroups: new Set(['core', 'presentation', 'filterPrimitive']), - attrs: new Set([ - 'class', - 'style', - // TODO: in - 'If no value is provided and this is the first filter primitive, - // then this filter primitive will use SourceGraphic as its input' - 'in', - 'in2', - 'mode', - ]), - defaults: { - mode: 'normal', - }, - content: new Set(['animate', 'set']), - }, - feColorMatrix: { - attrsGroups: new Set(['core', 'presentation', 'filterPrimitive']), - attrs: new Set(['class', 'style', 'in', 'type', 'values']), - defaults: { - type: 'matrix', - }, - content: new Set(['animate', 'set']), - }, - feComponentTransfer: { - attrsGroups: new Set(['core', 'presentation', 'filterPrimitive']), - attrs: new Set(['class', 'style', 'in']), - content: new Set(['feFuncA', 'feFuncB', 'feFuncG', 'feFuncR']), - }, - feComposite: { - attrsGroups: new Set(['core', 'presentation', 'filterPrimitive']), - attrs: new Set([ - 'class', - 'in', - 'in2', - 'k1', - 'k2', - 'k3', - 'k4', - 'operator', - 'style', - ]), - defaults: { - operator: 'over', - k1: '0', - k2: '0', - k3: '0', - k4: '0', - }, - content: new Set(['animate', 'set']), - }, - feConvolveMatrix: { - attrsGroups: new Set(['core', 'presentation', 'filterPrimitive']), - attrs: new Set([ - 'class', - 'in', - 'kernelMatrix', - 'order', - 'style', - // TODO: divisor - 'The default value is the sum of all values in kernelMatrix, - // with the exception that if the sum is zero, then the divisor is set to 1' - 'bias', - 'divisor', - // TODO: targetX - 'By default, the convolution matrix is centered in X over each - // pixel of the input image (i.e., targetX = floor ( orderX / 2 ))' - 'edgeMode', - 'targetX', - 'targetY', - // TODO: kernelUnitLength - 'The first number is the <dx> value. The second number - // is the <dy> value. If the <dy> value is not specified, it defaults to the same value as <dx>' - 'kernelUnitLength', - 'preserveAlpha', - ]), - defaults: { - order: '3', - bias: '0', - edgeMode: 'duplicate', - preserveAlpha: 'false', - }, - content: new Set(['animate', 'set']), - }, - feDiffuseLighting: { - attrsGroups: new Set(['core', 'presentation', 'filterPrimitive']), - attrs: new Set([ - 'class', - 'diffuseConstant', - 'in', - 'kernelUnitLength', - 'style', - 'surfaceScale', - ]), - defaults: { - surfaceScale: '1', - diffuseConstant: '1', - }, - contentGroups: new Set(['descriptive']), - content: new Set([ - // TODO: 'exactly one light source element, in any order' - 'feDistantLight', - 'fePointLight', - 'feSpotLight', - ]), - }, - feDisplacementMap: { - attrsGroups: new Set(['core', 'presentation', 'filterPrimitive']), - attrs: new Set([ - 'class', - 'in', - 'in2', - 'scale', - 'style', - 'xChannelSelector', - 'yChannelSelector', - ]), - defaults: { - scale: '0', - xChannelSelector: 'A', - yChannelSelector: 'A', - }, - content: new Set(['animate', 'set']), - }, - feDistantLight: { - attrsGroups: new Set(['core']), - attrs: new Set(['azimuth', 'elevation']), - defaults: { - azimuth: '0', - elevation: '0', - }, - content: new Set(['animate', 'set']), - }, - feFlood: { - attrsGroups: new Set(['core', 'presentation', 'filterPrimitive']), - attrs: new Set(['class', 'style']), - content: new Set(['animate', 'animateColor', 'set']), - }, - feFuncA: { - attrsGroups: new Set(['core', 'transferFunction']), - content: new Set(['set', 'animate']), - }, - feFuncB: { - attrsGroups: new Set(['core', 'transferFunction']), - content: new Set(['set', 'animate']), - }, - feFuncG: { - attrsGroups: new Set(['core', 'transferFunction']), - content: new Set(['set', 'animate']), - }, - feFuncR: { - attrsGroups: new Set(['core', 'transferFunction']), - content: new Set(['set', 'animate']), - }, - feGaussianBlur: { - attrsGroups: new Set(['core', 'presentation', 'filterPrimitive']), - attrs: new Set(['class', 'style', 'in', 'stdDeviation']), - defaults: { - stdDeviation: '0', - }, - content: new Set(['set', 'animate']), - }, - feImage: { - attrsGroups: new Set(['core', 'presentation', 'filterPrimitive', 'xlink']), - attrs: new Set([ - 'class', - 'externalResourcesRequired', - 'href', - 'preserveAspectRatio', - 'style', - 'xlink:href', - ]), - defaults: { - preserveAspectRatio: 'xMidYMid meet', - }, - content: new Set(['animate', 'animateTransform', 'set']), - }, - feMerge: { - attrsGroups: new Set(['core', 'presentation', 'filterPrimitive']), - attrs: new Set(['class', 'style']), - content: new Set(['feMergeNode']), - }, - feMergeNode: { - attrsGroups: new Set(['core']), - attrs: new Set(['in']), - content: new Set(['animate', 'set']), - }, - feMorphology: { - attrsGroups: new Set(['core', 'presentation', 'filterPrimitive']), - attrs: new Set(['class', 'style', 'in', 'operator', 'radius']), - defaults: { - operator: 'erode', - radius: '0', - }, - content: new Set(['animate', 'set']), - }, - feOffset: { - attrsGroups: new Set(['core', 'presentation', 'filterPrimitive']), - attrs: new Set(['class', 'style', 'in', 'dx', 'dy']), - defaults: { - dx: '0', - dy: '0', - }, - content: new Set(['animate', 'set']), - }, - fePointLight: { - attrsGroups: new Set(['core']), - attrs: new Set(['x', 'y', 'z']), - defaults: { - x: '0', - y: '0', - z: '0', - }, - content: new Set(['animate', 'set']), - }, - feSpecularLighting: { - attrsGroups: new Set(['core', 'presentation', 'filterPrimitive']), - attrs: new Set([ - 'class', - 'in', - 'kernelUnitLength', - 'specularConstant', - 'specularExponent', - 'style', - 'surfaceScale', - ]), - defaults: { - surfaceScale: '1', - specularConstant: '1', - specularExponent: '1', - }, - contentGroups: new Set([ - 'descriptive', - // TODO: exactly one 'light source element' - 'lightSource', - ]), - }, - feSpotLight: { - attrsGroups: new Set(['core']), - attrs: new Set([ - 'limitingConeAngle', - 'pointsAtX', - 'pointsAtY', - 'pointsAtZ', - 'specularExponent', - 'x', - 'y', - 'z', - ]), - defaults: { - x: '0', - y: '0', - z: '0', - pointsAtX: '0', - pointsAtY: '0', - pointsAtZ: '0', - specularExponent: '1', - }, - content: new Set(['animate', 'set']), - }, - feTile: { - attrsGroups: new Set(['core', 'presentation', 'filterPrimitive']), - attrs: new Set(['class', 'style', 'in']), - content: new Set(['animate', 'set']), - }, - feTurbulence: { - attrsGroups: new Set(['core', 'presentation', 'filterPrimitive']), - attrs: new Set([ - 'baseFrequency', - 'class', - 'numOctaves', - 'seed', - 'stitchTiles', - 'style', - 'type', - ]), - defaults: { - baseFrequency: '0', - numOctaves: '1', - seed: '0', - stitchTiles: 'noStitch', - type: 'turbulence', - }, - content: new Set(['animate', 'set']), - }, - filter: { - attrsGroups: new Set(['core', 'presentation', 'xlink']), - attrs: new Set([ - 'class', - 'externalResourcesRequired', - 'filterRes', - 'filterUnits', - 'height', - 'href', - 'primitiveUnits', - 'style', - 'width', - 'x', - 'xlink:href', - 'y', - ]), - defaults: { - primitiveUnits: 'userSpaceOnUse', - x: '-10%', - y: '-10%', - width: '120%', - height: '120%', - }, - contentGroups: new Set(['descriptive', 'filterPrimitive']), - content: new Set(['animate', 'set']), - }, - font: { - attrsGroups: new Set(['core', 'presentation']), - attrs: new Set([ - 'class', - 'externalResourcesRequired', - 'horiz-adv-x', - 'horiz-origin-x', - 'horiz-origin-y', - 'style', - 'vert-adv-y', - 'vert-origin-x', - 'vert-origin-y', - ]), - defaults: { - 'horiz-origin-x': '0', - 'horiz-origin-y': '0', - }, - contentGroups: new Set(['descriptive']), - content: new Set(['font-face', 'glyph', 'hkern', 'missing-glyph', 'vkern']), - }, - 'font-face': { - attrsGroups: new Set(['core']), - attrs: new Set([ - 'font-family', - 'font-style', - 'font-variant', - 'font-weight', - 'font-stretch', - 'font-size', - 'unicode-range', - 'units-per-em', - 'panose-1', - 'stemv', - 'stemh', - 'slope', - 'cap-height', - 'x-height', - 'accent-height', - 'ascent', - 'descent', - 'widths', - 'bbox', - 'ideographic', - 'alphabetic', - 'mathematical', - 'hanging', - 'v-ideographic', - 'v-alphabetic', - 'v-mathematical', - 'v-hanging', - 'underline-position', - 'underline-thickness', - 'strikethrough-position', - 'strikethrough-thickness', - 'overline-position', - 'overline-thickness', - ]), - defaults: { - 'font-style': 'all', - 'font-variant': 'normal', - 'font-weight': 'all', - 'font-stretch': 'normal', - 'unicode-range': 'U+0-10FFFF', - 'units-per-em': '1000', - 'panose-1': '0 0 0 0 0 0 0 0 0 0', - slope: '0', - }, - contentGroups: new Set(['descriptive']), - content: new Set([ - // TODO: "at most one 'font-face-src' element" - 'font-face-src', - ]), - }, - // TODO: empty content - 'font-face-format': { - attrsGroups: new Set(['core']), - attrs: new Set(['string']), - }, - 'font-face-name': { - attrsGroups: new Set(['core']), - attrs: new Set(['name']), - }, - 'font-face-src': { - attrsGroups: new Set(['core']), - content: new Set(['font-face-name', 'font-face-uri']), - }, - 'font-face-uri': { - attrsGroups: new Set(['core', 'xlink']), - attrs: new Set(['href', 'xlink:href']), - content: new Set(['font-face-format']), - }, - foreignObject: { - attrsGroups: new Set([ - 'conditionalProcessing', - 'core', - 'graphicalEvent', - 'presentation', - ]), - attrs: new Set([ - 'class', - 'externalResourcesRequired', - 'height', - 'style', - 'transform', - 'width', - 'x', - 'y', - ]), - defaults: { - x: '0', - y: '0', - }, - }, - g: { - attrsGroups: new Set([ - 'conditionalProcessing', - 'core', - 'graphicalEvent', - 'presentation', - ]), - attrs: new Set([ - 'class', - 'externalResourcesRequired', - 'style', - 'transform', - ]), - contentGroups: new Set([ - 'animation', - 'descriptive', - 'paintServer', - 'shape', - 'structural', - ]), - content: new Set([ - 'a', - 'altGlyphDef', - 'clipPath', - 'color-profile', - 'cursor', - 'filter', - 'font-face', - 'font', - 'foreignObject', - 'image', - 'marker', - 'mask', - 'pattern', - 'script', - 'style', - 'switch', - 'text', - 'view', - ]), - }, - glyph: { - attrsGroups: new Set(['core', 'presentation']), - attrs: new Set([ - 'arabic-form', - 'class', - 'd', - 'glyph-name', - 'horiz-adv-x', - 'lang', - 'orientation', - 'style', - 'unicode', - 'vert-adv-y', - 'vert-origin-x', - 'vert-origin-y', - ]), - defaults: { - 'arabic-form': 'initial', - }, - contentGroups: new Set([ - 'animation', - 'descriptive', - 'paintServer', - 'shape', - 'structural', - ]), - content: new Set([ - 'a', - 'altGlyphDef', - 'clipPath', - 'color-profile', - 'cursor', - 'filter', - 'font-face', - 'font', - 'foreignObject', - 'image', - 'marker', - 'mask', - 'pattern', - 'script', - 'style', - 'switch', - 'text', - 'view', - ]), - }, - glyphRef: { - attrsGroups: new Set(['core', 'presentation']), - attrs: new Set([ - 'class', - 'd', - 'horiz-adv-x', - 'style', - 'vert-adv-y', - 'vert-origin-x', - 'vert-origin-y', - ]), - contentGroups: new Set([ - 'animation', - 'descriptive', - 'paintServer', - 'shape', - 'structural', - ]), - content: new Set([ - 'a', - 'altGlyphDef', - 'clipPath', - 'color-profile', - 'cursor', - 'filter', - 'font-face', - 'font', - 'foreignObject', - 'image', - 'marker', - 'mask', - 'pattern', - 'script', - 'style', - 'switch', - 'text', - 'view', - ]), - }, - hatch: { - attrsGroups: new Set(['core', 'presentation', 'xlink']), - attrs: new Set([ - 'class', - 'hatchContentUnits', - 'hatchUnits', - 'pitch', - 'rotate', - 'style', - 'transform', - 'x', - 'y', - ]), - defaults: { - hatchUnits: 'objectBoundingBox', - hatchContentUnits: 'userSpaceOnUse', - x: '0', - y: '0', - pitch: '0', - rotate: '0', - }, - contentGroups: new Set(['animation', 'descriptive']), - content: new Set(['hatchPath']), - }, - hatchPath: { - attrsGroups: new Set(['core', 'presentation', 'xlink']), - attrs: new Set(['class', 'style', 'd', 'offset']), - defaults: { - offset: '0', - }, - contentGroups: new Set(['animation', 'descriptive']), - }, - hkern: { - attrsGroups: new Set(['core']), - attrs: new Set(['u1', 'g1', 'u2', 'g2', 'k']), - }, - image: { - attrsGroups: new Set([ - 'conditionalProcessing', - 'core', - 'graphicalEvent', - 'presentation', - 'xlink', - ]), - attrs: new Set([ - 'class', - 'externalResourcesRequired', - 'height', - 'href', - 'preserveAspectRatio', - 'style', - 'transform', - 'width', - 'x', - 'xlink:href', - 'y', - ]), - defaults: { - x: '0', - y: '0', - preserveAspectRatio: 'xMidYMid meet', - }, - contentGroups: new Set(['animation', 'descriptive']), - }, - line: { - attrsGroups: new Set([ - 'conditionalProcessing', - 'core', - 'graphicalEvent', - 'presentation', - ]), - attrs: new Set([ - 'class', - 'externalResourcesRequired', - 'style', - 'transform', - 'x1', - 'x2', - 'y1', - 'y2', - ]), - defaults: { - x1: '0', - y1: '0', - x2: '0', - y2: '0', - }, - contentGroups: new Set(['animation', 'descriptive']), - }, - linearGradient: { - attrsGroups: new Set(['core', 'presentation', 'xlink']), - attrs: new Set([ - 'class', - 'externalResourcesRequired', - 'gradientTransform', - 'gradientUnits', - 'href', - 'spreadMethod', - 'style', - 'x1', - 'x2', - 'xlink:href', - 'y1', - 'y2', - ]), - defaults: { - x1: '0', - y1: '0', - x2: '100%', - y2: '0', - spreadMethod: 'pad', - }, - contentGroups: new Set(['descriptive']), - content: new Set(['animate', 'animateTransform', 'set', 'stop']), - }, - marker: { - attrsGroups: new Set(['core', 'presentation']), - attrs: new Set([ - 'class', - 'externalResourcesRequired', - 'markerHeight', - 'markerUnits', - 'markerWidth', - 'orient', - 'preserveAspectRatio', - 'refX', - 'refY', - 'style', - 'viewBox', - ]), - defaults: { - markerUnits: 'strokeWidth', - refX: '0', - refY: '0', - markerWidth: '3', - markerHeight: '3', - }, - contentGroups: new Set([ - 'animation', - 'descriptive', - 'paintServer', - 'shape', - 'structural', - ]), - content: new Set([ - 'a', - 'altGlyphDef', - 'clipPath', - 'color-profile', - 'cursor', - 'filter', - 'font-face', - 'font', - 'foreignObject', - 'image', - 'marker', - 'mask', - 'pattern', - 'script', - 'style', - 'switch', - 'text', - 'view', - ]), - }, - mask: { - attrsGroups: new Set(['conditionalProcessing', 'core', 'presentation']), - attrs: new Set([ - 'class', - 'externalResourcesRequired', - 'height', - 'mask-type', - 'maskContentUnits', - 'maskUnits', - 'style', - 'width', - 'x', - 'y', - ]), - defaults: { - maskUnits: 'objectBoundingBox', - maskContentUnits: 'userSpaceOnUse', - x: '-10%', - y: '-10%', - width: '120%', - height: '120%', - }, - contentGroups: new Set([ - 'animation', - 'descriptive', - 'paintServer', - 'shape', - 'structural', - ]), - content: new Set([ - 'a', - 'altGlyphDef', - 'clipPath', - 'color-profile', - 'cursor', - 'filter', - 'font-face', - 'font', - 'foreignObject', - 'image', - 'marker', - 'mask', - 'pattern', - 'script', - 'style', - 'switch', - 'text', - 'view', - ]), - }, - metadata: { - attrsGroups: new Set(['core']), - }, - 'missing-glyph': { - attrsGroups: new Set(['core', 'presentation']), - attrs: new Set([ - 'class', - 'd', - 'horiz-adv-x', - 'style', - 'vert-adv-y', - 'vert-origin-x', - 'vert-origin-y', - ]), - contentGroups: new Set([ - 'animation', - 'descriptive', - 'paintServer', - 'shape', - 'structural', - ]), - content: new Set([ - 'a', - 'altGlyphDef', - 'clipPath', - 'color-profile', - 'cursor', - 'filter', - 'font-face', - 'font', - 'foreignObject', - 'image', - 'marker', - 'mask', - 'pattern', - 'script', - 'style', - 'switch', - 'text', - 'view', - ]), - }, - mpath: { - attrsGroups: new Set(['core', 'xlink']), - attrs: new Set(['externalResourcesRequired', 'href', 'xlink:href']), - contentGroups: new Set(['descriptive']), - }, - path: { - attrsGroups: new Set([ - 'conditionalProcessing', - 'core', - 'graphicalEvent', - 'presentation', - ]), - attrs: new Set([ - 'class', - 'd', - 'externalResourcesRequired', - 'pathLength', - 'style', - 'transform', - ]), - contentGroups: new Set(['animation', 'descriptive']), - }, - pattern: { - attrsGroups: new Set([ - 'conditionalProcessing', - 'core', - 'presentation', - 'xlink', - ]), - attrs: new Set([ - 'class', - 'externalResourcesRequired', - 'height', - 'href', - 'patternContentUnits', - 'patternTransform', - 'patternUnits', - 'preserveAspectRatio', - 'style', - 'viewBox', - 'width', - 'x', - 'xlink:href', - 'y', - ]), - defaults: { - patternUnits: 'objectBoundingBox', - patternContentUnits: 'userSpaceOnUse', - x: '0', - y: '0', - width: '0', - height: '0', - preserveAspectRatio: 'xMidYMid meet', - }, - contentGroups: new Set([ - 'animation', - 'descriptive', - 'paintServer', - 'shape', - 'structural', - ]), - content: new Set([ - 'a', - 'altGlyphDef', - 'clipPath', - 'color-profile', - 'cursor', - 'filter', - 'font-face', - 'font', - 'foreignObject', - 'image', - 'marker', - 'mask', - 'pattern', - 'script', - 'style', - 'switch', - 'text', - 'view', - ]), - }, - polygon: { - attrsGroups: new Set([ - 'conditionalProcessing', - 'core', - 'graphicalEvent', - 'presentation', - ]), - attrs: new Set([ - 'class', - 'externalResourcesRequired', - 'points', - 'style', - 'transform', - ]), - contentGroups: new Set(['animation', 'descriptive']), - }, - polyline: { - attrsGroups: new Set([ - 'conditionalProcessing', - 'core', - 'graphicalEvent', - 'presentation', - ]), - attrs: new Set([ - 'class', - 'externalResourcesRequired', - 'points', - 'style', - 'transform', - ]), - contentGroups: new Set(['animation', 'descriptive']), - }, - radialGradient: { - attrsGroups: new Set(['core', 'presentation', 'xlink']), - attrs: new Set([ - 'class', - 'cx', - 'cy', - 'externalResourcesRequired', - 'fr', - 'fx', - 'fy', - 'gradientTransform', - 'gradientUnits', - 'href', - 'r', - 'spreadMethod', - 'style', - 'xlink:href', - ]), - defaults: { - gradientUnits: 'objectBoundingBox', - cx: '50%', - cy: '50%', - r: '50%', - }, - contentGroups: new Set(['descriptive']), - content: new Set(['animate', 'animateTransform', 'set', 'stop']), - }, - meshGradient: { - attrsGroups: new Set(['core', 'presentation', 'xlink']), - attrs: new Set(['class', 'style', 'x', 'y', 'gradientUnits', 'transform']), - contentGroups: new Set(['descriptive', 'paintServer', 'animation']), - content: new Set(['meshRow']), - }, - meshRow: { - attrsGroups: new Set(['core', 'presentation']), - attrs: new Set(['class', 'style']), - contentGroups: new Set(['descriptive']), - content: new Set(['meshPatch']), - }, - meshPatch: { - attrsGroups: new Set(['core', 'presentation']), - attrs: new Set(['class', 'style']), - contentGroups: new Set(['descriptive']), - content: new Set(['stop']), - }, - rect: { - attrsGroups: new Set([ - 'conditionalProcessing', - 'core', - 'graphicalEvent', - 'presentation', - ]), - attrs: new Set([ - 'class', - 'externalResourcesRequired', - 'height', - 'rx', - 'ry', - 'style', - 'transform', - 'width', - 'x', - 'y', - ]), - defaults: { - x: '0', - y: '0', - }, - contentGroups: new Set(['animation', 'descriptive']), - }, - script: { - attrsGroups: new Set(['core', 'xlink']), - attrs: new Set(['externalResourcesRequired', 'type', 'href', 'xlink:href']), - }, - set: { - attrsGroups: new Set([ - 'animation', - 'animationAttributeTarget', - 'animationTiming', - 'conditionalProcessing', - 'core', - 'xlink', - ]), - attrs: new Set(['externalResourcesRequired', 'to']), - contentGroups: new Set(['descriptive']), - }, - solidColor: { - attrsGroups: new Set(['core', 'presentation']), - attrs: new Set(['class', 'style']), - contentGroups: new Set(['paintServer']), - }, - stop: { - attrsGroups: new Set(['core', 'presentation']), - attrs: new Set(['class', 'style', 'offset', 'path']), - content: new Set(['animate', 'animateColor', 'set']), - }, - style: { - attrsGroups: new Set(['core']), - attrs: new Set(['type', 'media', 'title']), - defaults: { - type: 'text/css', - }, - }, - svg: { - attrsGroups: new Set([ - 'conditionalProcessing', - 'core', - 'documentEvent', - 'graphicalEvent', - 'presentation', - ]), - attrs: new Set([ - 'baseProfile', - 'class', - 'contentScriptType', - 'contentStyleType', - 'height', - 'preserveAspectRatio', - 'style', - 'version', - 'viewBox', - 'width', - 'x', - 'y', - 'zoomAndPan', - ]), - defaults: { - x: '0', - y: '0', - width: '100%', - height: '100%', - preserveAspectRatio: 'xMidYMid meet', - zoomAndPan: 'magnify', - version: '1.1', - baseProfile: 'none', - contentScriptType: 'application/ecmascript', - contentStyleType: 'text/css', - }, - contentGroups: new Set([ - 'animation', - 'descriptive', - 'paintServer', - 'shape', - 'structural', - ]), - content: new Set([ - 'a', - 'altGlyphDef', - 'clipPath', - 'color-profile', - 'cursor', - 'filter', - 'font-face', - 'font', - 'foreignObject', - 'image', - 'marker', - 'mask', - 'pattern', - 'script', - 'style', - 'switch', - 'text', - 'view', - ]), - }, - switch: { - attrsGroups: new Set([ - 'conditionalProcessing', - 'core', - 'graphicalEvent', - 'presentation', - ]), - attrs: new Set([ - 'class', - 'externalResourcesRequired', - 'style', - 'transform', - ]), - contentGroups: new Set(['animation', 'descriptive', 'shape']), - content: new Set([ - 'a', - 'foreignObject', - 'g', - 'image', - 'svg', - 'switch', - 'text', - 'use', - ]), - }, - symbol: { - attrsGroups: new Set(['core', 'graphicalEvent', 'presentation']), - attrs: new Set([ - 'class', - 'externalResourcesRequired', - 'preserveAspectRatio', - 'refX', - 'refY', - 'style', - 'viewBox', - ]), - defaults: { - refX: '0', - refY: '0', - }, - contentGroups: new Set([ - 'animation', - 'descriptive', - 'paintServer', - 'shape', - 'structural', - ]), - content: new Set([ - 'a', - 'altGlyphDef', - 'clipPath', - 'color-profile', - 'cursor', - 'filter', - 'font-face', - 'font', - 'foreignObject', - 'image', - 'marker', - 'mask', - 'pattern', - 'script', - 'style', - 'switch', - 'text', - 'view', - ]), - }, - text: { - attrsGroups: new Set([ - 'conditionalProcessing', - 'core', - 'graphicalEvent', - 'presentation', - ]), - attrs: new Set([ - 'class', - 'dx', - 'dy', - 'externalResourcesRequired', - 'lengthAdjust', - 'rotate', - 'style', - 'textLength', - 'transform', - 'x', - 'y', - ]), - defaults: { - x: '0', - y: '0', - lengthAdjust: 'spacing', - }, - contentGroups: new Set(['animation', 'descriptive', 'textContentChild']), - content: new Set(['a']), - }, - textPath: { - attrsGroups: new Set([ - 'conditionalProcessing', - 'core', - 'graphicalEvent', - 'presentation', - 'xlink', - ]), - attrs: new Set([ - 'class', - 'd', - 'externalResourcesRequired', - 'href', - 'method', - 'spacing', - 'startOffset', - 'style', - 'xlink:href', - ]), - defaults: { - startOffset: '0', - method: 'align', - spacing: 'exact', - }, - contentGroups: new Set(['descriptive']), - content: new Set([ - 'a', - 'altGlyph', - 'animate', - 'animateColor', - 'set', - 'tref', - 'tspan', - ]), - }, - title: { - attrsGroups: new Set(['core']), - attrs: new Set(['class', 'style']), - }, - tref: { - attrsGroups: new Set([ - 'conditionalProcessing', - 'core', - 'graphicalEvent', - 'presentation', - 'xlink', - ]), - attrs: new Set([ - 'class', - 'externalResourcesRequired', - 'href', - 'style', - 'xlink:href', - ]), - contentGroups: new Set(['descriptive']), - content: new Set(['animate', 'animateColor', 'set']), - }, - tspan: { - attrsGroups: new Set([ - 'conditionalProcessing', - 'core', - 'graphicalEvent', - 'presentation', - ]), - attrs: new Set([ - 'class', - 'dx', - 'dy', - 'externalResourcesRequired', - 'lengthAdjust', - 'rotate', - 'style', - 'textLength', - 'x', - 'y', - ]), - contentGroups: new Set(['descriptive']), - content: new Set([ - 'a', - 'altGlyph', - 'animate', - 'animateColor', - 'set', - 'tref', - 'tspan', - ]), - }, - use: { - attrsGroups: new Set([ - 'conditionalProcessing', - 'core', - 'graphicalEvent', - 'presentation', - 'xlink', - ]), - attrs: new Set([ - 'class', - 'externalResourcesRequired', - 'height', - 'href', - 'style', - 'transform', - 'width', - 'x', - 'xlink:href', - 'y', - ]), - defaults: { - x: '0', - y: '0', - }, - contentGroups: new Set(['animation', 'descriptive']), - }, - view: { - attrsGroups: new Set(['core']), - attrs: new Set([ - 'externalResourcesRequired', - 'preserveAspectRatio', - 'viewBox', - 'viewTarget', - 'zoomAndPan', - ]), - contentGroups: new Set(['descriptive']), - }, - vkern: { - attrsGroups: new Set(['core']), - attrs: new Set(['u1', 'g1', 'u2', 'g2', 'k']), - }, -}; - -// https://wiki.inkscape.org/wiki/index.php/Inkscape-specific_XML_attributes -exports.editorNamespaces = new Set([ - 'http://creativecommons.org/ns#', - 'http://inkscape.sourceforge.net/DTD/sodipodi-0.dtd', - 'http://ns.adobe.com/AdobeIllustrator/10.0/', - 'http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/', - 'http://ns.adobe.com/Extensibility/1.0/', - 'http://ns.adobe.com/Flows/1.0/', - 'http://ns.adobe.com/GenericCustomNamespace/1.0/', - 'http://ns.adobe.com/Graphs/1.0/', - 'http://ns.adobe.com/ImageReplacement/1.0/', - 'http://ns.adobe.com/SaveForWeb/1.0/', - 'http://ns.adobe.com/Variables/1.0/', - 'http://ns.adobe.com/XPath/1.0/', - 'http://purl.org/dc/elements/1.1/', - 'http://schemas.microsoft.com/visio/2003/SVGExtensions/', - 'http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd', - 'http://taptrix.com/vectorillustrator/svg_extensions', - 'http://www.bohemiancoding.com/sketch/ns', - 'http://www.figma.com/figma/ns', - 'http://www.inkscape.org/namespaces/inkscape', - 'http://www.serif.com/', - 'http://www.vector.evaxdesign.sk', - 'http://www.w3.org/1999/02/22-rdf-syntax-ns#', -]); - -/** - * @see https://www.w3.org/TR/SVG11/linking.html#processingIRI - */ -exports.referencesProps = new Set([ - 'clip-path', - 'color-profile', - 'fill', - 'filter', - 'marker-end', - 'marker-mid', - 'marker-start', - 'mask', - 'stroke', - 'style', -]); - -/** - * @see https://www.w3.org/TR/SVG11/propidx.html - */ -exports.inheritableAttrs = new Set([ - 'clip-rule', - 'color-interpolation-filters', - 'color-interpolation', - 'color-profile', - 'color-rendering', - 'color', - 'cursor', - 'direction', - 'dominant-baseline', - 'fill-opacity', - 'fill-rule', - 'fill', - 'font-family', - 'font-size-adjust', - 'font-size', - 'font-stretch', - 'font-style', - 'font-variant', - 'font-weight', - 'font', - 'glyph-orientation-horizontal', - 'glyph-orientation-vertical', - 'image-rendering', - 'letter-spacing', - 'marker-end', - 'marker-mid', - 'marker-start', - 'marker', - 'paint-order', - 'pointer-events', - 'shape-rendering', - 'stroke-dasharray', - 'stroke-dashoffset', - 'stroke-linecap', - 'stroke-linejoin', - 'stroke-miterlimit', - 'stroke-opacity', - 'stroke-width', - 'stroke', - 'text-anchor', - 'text-rendering', - 'transform', - 'visibility', - 'word-spacing', - 'writing-mode', -]); - -exports.presentationNonInheritableGroupAttrs = new Set([ - 'clip-path', - 'display', - 'filter', - 'mask', - 'opacity', - 'text-decoration', - 'transform', - 'unicode-bidi', -]); - -/** - * https://www.w3.org/TR/SVG11/single-page.html#types-ColorKeywords - * - * @type {Record<string, string>} - */ -exports.colorsNames = { - aliceblue: '#f0f8ff', - antiquewhite: '#faebd7', - aqua: '#0ff', - aquamarine: '#7fffd4', - azure: '#f0ffff', - beige: '#f5f5dc', - bisque: '#ffe4c4', - black: '#000', - blanchedalmond: '#ffebcd', - blue: '#00f', - blueviolet: '#8a2be2', - brown: '#a52a2a', - burlywood: '#deb887', - cadetblue: '#5f9ea0', - chartreuse: '#7fff00', - chocolate: '#d2691e', - coral: '#ff7f50', - cornflowerblue: '#6495ed', - cornsilk: '#fff8dc', - crimson: '#dc143c', - cyan: '#0ff', - darkblue: '#00008b', - darkcyan: '#008b8b', - darkgoldenrod: '#b8860b', - darkgray: '#a9a9a9', - darkgreen: '#006400', - darkgrey: '#a9a9a9', - darkkhaki: '#bdb76b', - darkmagenta: '#8b008b', - darkolivegreen: '#556b2f', - darkorange: '#ff8c00', - darkorchid: '#9932cc', - darkred: '#8b0000', - darksalmon: '#e9967a', - darkseagreen: '#8fbc8f', - darkslateblue: '#483d8b', - darkslategray: '#2f4f4f', - darkslategrey: '#2f4f4f', - darkturquoise: '#00ced1', - darkviolet: '#9400d3', - deeppink: '#ff1493', - deepskyblue: '#00bfff', - dimgray: '#696969', - dimgrey: '#696969', - dodgerblue: '#1e90ff', - firebrick: '#b22222', - floralwhite: '#fffaf0', - forestgreen: '#228b22', - fuchsia: '#f0f', - gainsboro: '#dcdcdc', - ghostwhite: '#f8f8ff', - gold: '#ffd700', - goldenrod: '#daa520', - gray: '#808080', - green: '#008000', - greenyellow: '#adff2f', - grey: '#808080', - honeydew: '#f0fff0', - hotpink: '#ff69b4', - indianred: '#cd5c5c', - indigo: '#4b0082', - ivory: '#fffff0', - khaki: '#f0e68c', - lavender: '#e6e6fa', - lavenderblush: '#fff0f5', - lawngreen: '#7cfc00', - lemonchiffon: '#fffacd', - lightblue: '#add8e6', - lightcoral: '#f08080', - lightcyan: '#e0ffff', - lightgoldenrodyellow: '#fafad2', - lightgray: '#d3d3d3', - lightgreen: '#90ee90', - lightgrey: '#d3d3d3', - lightpink: '#ffb6c1', - lightsalmon: '#ffa07a', - lightseagreen: '#20b2aa', - lightskyblue: '#87cefa', - lightslategray: '#789', - lightslategrey: '#789', - lightsteelblue: '#b0c4de', - lightyellow: '#ffffe0', - lime: '#0f0', - limegreen: '#32cd32', - linen: '#faf0e6', - magenta: '#f0f', - maroon: '#800000', - mediumaquamarine: '#66cdaa', - mediumblue: '#0000cd', - mediumorchid: '#ba55d3', - mediumpurple: '#9370db', - mediumseagreen: '#3cb371', - mediumslateblue: '#7b68ee', - mediumspringgreen: '#00fa9a', - mediumturquoise: '#48d1cc', - mediumvioletred: '#c71585', - midnightblue: '#191970', - mintcream: '#f5fffa', - mistyrose: '#ffe4e1', - moccasin: '#ffe4b5', - navajowhite: '#ffdead', - navy: '#000080', - oldlace: '#fdf5e6', - olive: '#808000', - olivedrab: '#6b8e23', - orange: '#ffa500', - orangered: '#ff4500', - orchid: '#da70d6', - palegoldenrod: '#eee8aa', - palegreen: '#98fb98', - paleturquoise: '#afeeee', - palevioletred: '#db7093', - papayawhip: '#ffefd5', - peachpuff: '#ffdab9', - peru: '#cd853f', - pink: '#ffc0cb', - plum: '#dda0dd', - powderblue: '#b0e0e6', - purple: '#800080', - rebeccapurple: '#639', - red: '#f00', - rosybrown: '#bc8f8f', - royalblue: '#4169e1', - saddlebrown: '#8b4513', - salmon: '#fa8072', - sandybrown: '#f4a460', - seagreen: '#2e8b57', - seashell: '#fff5ee', - sienna: '#a0522d', - silver: '#c0c0c0', - skyblue: '#87ceeb', - slateblue: '#6a5acd', - slategray: '#708090', - slategrey: '#708090', - snow: '#fffafa', - springgreen: '#00ff7f', - steelblue: '#4682b4', - tan: '#d2b48c', - teal: '#008080', - thistle: '#d8bfd8', - tomato: '#ff6347', - turquoise: '#40e0d0', - violet: '#ee82ee', - wheat: '#f5deb3', - white: '#fff', - whitesmoke: '#f5f5f5', - yellow: '#ff0', - yellowgreen: '#9acd32', -}; - -/** - * @type {Record<string, string>} - */ -exports.colorsShortNames = { - '#f0ffff': 'azure', - '#f5f5dc': 'beige', - '#ffe4c4': 'bisque', - '#a52a2a': 'brown', - '#ff7f50': 'coral', - '#ffd700': 'gold', - '#808080': 'gray', - '#008000': 'green', - '#4b0082': 'indigo', - '#fffff0': 'ivory', - '#f0e68c': 'khaki', - '#faf0e6': 'linen', - '#800000': 'maroon', - '#000080': 'navy', - '#808000': 'olive', - '#ffa500': 'orange', - '#da70d6': 'orchid', - '#cd853f': 'peru', - '#ffc0cb': 'pink', - '#dda0dd': 'plum', - '#800080': 'purple', - '#f00': 'red', - '#ff0000': 'red', - '#fa8072': 'salmon', - '#a0522d': 'sienna', - '#c0c0c0': 'silver', - '#fffafa': 'snow', - '#d2b48c': 'tan', - '#008080': 'teal', - '#ff6347': 'tomato', - '#ee82ee': 'violet', - '#f5deb3': 'wheat', -}; - -/** - * @see https://www.w3.org/TR/SVG11/single-page.html#types-DataTypeColor - */ -exports.colorsProps = new Set([ - 'color', - 'fill', - 'flood-color', - 'lighting-color', - 'stop-color', - 'stroke', -]); - -/** @see https://developer.mozilla.org/docs/Web/CSS/Pseudo-classes */ -exports.pseudoClasses = { - displayState: new Set(['fullscreen', 'modal', 'picture-in-picture']), - input: new Set([ - 'autofill', - 'blank', - 'checked', - 'default', - 'disabled', - 'enabled', - 'in-range', - 'indetermined', - 'invalid', - 'optional', - 'out-of-range', - 'placeholder-shown', - 'read-only', - 'read-write', - 'required', - 'user-invalid', - 'valid', - ]), - linguistic: new Set(['dir', 'lang']), - location: new Set([ - 'any-link', - 'link', - 'local-link', - 'scope', - 'target-within', - 'target', - 'visited', - ]), - resourceState: new Set(['playing', 'paused']), - timeDimensional: new Set(['current', 'past', 'future']), - treeStructural: new Set([ - 'empty', - 'first-child', - 'first-of-type', - 'last-child', - 'last-of-type', - 'nth-child', - 'nth-last-child', - 'nth-last-of-type', - 'nth-of-type', - 'only-child', - 'only-of-type', - 'root', - ]), - userAction: new Set([ - 'active', - 'focus-visible', - 'focus-within', - 'focus', - 'hover', - ]), - functional: new Set(['is', 'not', 'where', 'has']), -}; diff --git a/node_modules/svgo/plugins/_path.js b/node_modules/svgo/plugins/_path.js deleted file mode 100644 index 7c3a28b9..00000000 --- a/node_modules/svgo/plugins/_path.js +++ /dev/null @@ -1,815 +0,0 @@ -'use strict'; - -/** - * @typedef {import('../lib/types').XastElement} XastElement - * @typedef {import('../lib/types').PathDataItem} PathDataItem - */ - -const { parsePathData, stringifyPathData } = require('../lib/path.js'); - -/** - * @type {[number, number]} - */ -var prevCtrlPoint; - -/** - * Convert path string to JS representation. - * - * @type {(path: XastElement) => PathDataItem[]} - */ -const path2js = (path) => { - // @ts-ignore legacy - if (path.pathJS) return path.pathJS; - /** - * @type {PathDataItem[]} - */ - const pathData = []; // JS representation of the path data - const newPathData = parsePathData(path.attributes.d); - for (const { command, args } of newPathData) { - pathData.push({ command, args }); - } - // First moveto is actually absolute. Subsequent coordinates were separated above. - if (pathData.length && pathData[0].command == 'm') { - pathData[0].command = 'M'; - } - // @ts-ignore legacy - path.pathJS = pathData; - return pathData; -}; -exports.path2js = path2js; - -/** - * Convert relative Path data to absolute. - * - * @type {(data: PathDataItem[]) => PathDataItem[]} - * - */ -const convertRelativeToAbsolute = (data) => { - /** - * @type {PathDataItem[]} - */ - const newData = []; - let start = [0, 0]; - let cursor = [0, 0]; - - for (let { command, args } of data) { - args = args.slice(); - - // moveto (x y) - if (command === 'm') { - args[0] += cursor[0]; - args[1] += cursor[1]; - command = 'M'; - } - if (command === 'M') { - cursor[0] = args[0]; - cursor[1] = args[1]; - start[0] = cursor[0]; - start[1] = cursor[1]; - } - - // horizontal lineto (x) - if (command === 'h') { - args[0] += cursor[0]; - command = 'H'; - } - if (command === 'H') { - cursor[0] = args[0]; - } - - // vertical lineto (y) - if (command === 'v') { - args[0] += cursor[1]; - command = 'V'; - } - if (command === 'V') { - cursor[1] = args[0]; - } - - // lineto (x y) - if (command === 'l') { - args[0] += cursor[0]; - args[1] += cursor[1]; - command = 'L'; - } - if (command === 'L') { - cursor[0] = args[0]; - cursor[1] = args[1]; - } - - // curveto (x1 y1 x2 y2 x y) - if (command === 'c') { - args[0] += cursor[0]; - args[1] += cursor[1]; - args[2] += cursor[0]; - args[3] += cursor[1]; - args[4] += cursor[0]; - args[5] += cursor[1]; - command = 'C'; - } - if (command === 'C') { - cursor[0] = args[4]; - cursor[1] = args[5]; - } - - // smooth curveto (x2 y2 x y) - if (command === 's') { - args[0] += cursor[0]; - args[1] += cursor[1]; - args[2] += cursor[0]; - args[3] += cursor[1]; - command = 'S'; - } - if (command === 'S') { - cursor[0] = args[2]; - cursor[1] = args[3]; - } - - // quadratic Bézier curveto (x1 y1 x y) - if (command === 'q') { - args[0] += cursor[0]; - args[1] += cursor[1]; - args[2] += cursor[0]; - args[3] += cursor[1]; - command = 'Q'; - } - if (command === 'Q') { - cursor[0] = args[2]; - cursor[1] = args[3]; - } - - // smooth quadratic Bézier curveto (x y) - if (command === 't') { - args[0] += cursor[0]; - args[1] += cursor[1]; - command = 'T'; - } - if (command === 'T') { - cursor[0] = args[0]; - cursor[1] = args[1]; - } - - // elliptical arc (rx ry x-axis-rotation large-arc-flag sweep-flag x y) - if (command === 'a') { - args[5] += cursor[0]; - args[6] += cursor[1]; - command = 'A'; - } - if (command === 'A') { - cursor[0] = args[5]; - cursor[1] = args[6]; - } - - // closepath - if (command === 'z' || command === 'Z') { - cursor[0] = start[0]; - cursor[1] = start[1]; - command = 'z'; - } - - newData.push({ command, args }); - } - return newData; -}; - -/** - * @typedef {{ floatPrecision?: number, noSpaceAfterFlags?: boolean }} Js2PathParams - */ - -/** - * Convert path array to string. - * - * @type {(path: XastElement, data: PathDataItem[], params: Js2PathParams) => void} - */ -exports.js2path = function (path, data, params) { - // @ts-ignore legacy - path.pathJS = data; - - const pathData = []; - for (const item of data) { - // remove moveto commands which are followed by moveto commands - if ( - pathData.length !== 0 && - (item.command === 'M' || item.command === 'm') - ) { - const last = pathData[pathData.length - 1]; - if (last.command === 'M' || last.command === 'm') { - pathData.pop(); - } - } - pathData.push({ - command: item.command, - args: item.args, - }); - } - - path.attributes.d = stringifyPathData({ - pathData, - precision: params.floatPrecision, - disableSpaceAfterFlags: params.noSpaceAfterFlags, - }); -}; - -/** - * @type {(dest: number[], source: number[]) => number[]} - */ -function set(dest, source) { - dest[0] = source[source.length - 2]; - dest[1] = source[source.length - 1]; - return dest; -} - -/** - * Checks if two paths have an intersection by checking convex hulls - * collision using Gilbert-Johnson-Keerthi distance algorithm - * https://web.archive.org/web/20180822200027/http://entropyinteractive.com/2011/04/gjk-algorithm/ - * - * @type {(path1: PathDataItem[], path2: PathDataItem[]) => boolean} - */ -exports.intersects = function (path1, path2) { - // Collect points of every subpath. - const points1 = gatherPoints(convertRelativeToAbsolute(path1)); - const points2 = gatherPoints(convertRelativeToAbsolute(path2)); - - // Axis-aligned bounding box check. - if ( - points1.maxX <= points2.minX || - points2.maxX <= points1.minX || - points1.maxY <= points2.minY || - points2.maxY <= points1.minY || - points1.list.every((set1) => { - return points2.list.every((set2) => { - return ( - set1.list[set1.maxX][0] <= set2.list[set2.minX][0] || - set2.list[set2.maxX][0] <= set1.list[set1.minX][0] || - set1.list[set1.maxY][1] <= set2.list[set2.minY][1] || - set2.list[set2.maxY][1] <= set1.list[set1.minY][1] - ); - }); - }) - ) - return false; - - // Get a convex hull from points of each subpath. Has the most complexity O(n·log n). - const hullNest1 = points1.list.map(convexHull); - const hullNest2 = points2.list.map(convexHull); - - // Check intersection of every subpath of the first path with every subpath of the second. - return hullNest1.some(function (hull1) { - if (hull1.list.length < 3) return false; - - return hullNest2.some(function (hull2) { - if (hull2.list.length < 3) return false; - - var simplex = [getSupport(hull1, hull2, [1, 0])], // create the initial simplex - direction = minus(simplex[0]); // set the direction to point towards the origin - - var iterations = 1e4; // infinite loop protection, 10 000 iterations is more than enough - // eslint-disable-next-line no-constant-condition - while (true) { - if (iterations-- == 0) { - console.error( - 'Error: infinite loop while processing mergePaths plugin.', - ); - return true; // true is the safe value that means “do nothing with paths” - } - // add a new point - simplex.push(getSupport(hull1, hull2, direction)); - // see if the new point was on the correct side of the origin - if (dot(direction, simplex[simplex.length - 1]) <= 0) return false; - // process the simplex - if (processSimplex(simplex, direction)) return true; - } - }); - }); - - /** - * @type {(a: Point, b: Point, direction: number[]) => number[]} - */ - function getSupport(a, b, direction) { - return sub(supportPoint(a, direction), supportPoint(b, minus(direction))); - } - - // Computes farthest polygon point in particular direction. - // Thanks to knowledge of min/max x and y coordinates we can choose a quadrant to search in. - // Since we're working on convex hull, the dot product is increasing until we find the farthest point. - /** - * @type {(polygon: Point, direction: number[]) => number[]} - */ - function supportPoint(polygon, direction) { - var index = - direction[1] >= 0 - ? direction[0] < 0 - ? polygon.maxY - : polygon.maxX - : direction[0] < 0 - ? polygon.minX - : polygon.minY, - max = -Infinity, - value; - while ((value = dot(polygon.list[index], direction)) > max) { - max = value; - index = ++index % polygon.list.length; - } - return polygon.list[(index || polygon.list.length) - 1]; - } -}; - -/** - * @type {(simplex: number[][], direction: number[]) => boolean} - */ -function processSimplex(simplex, direction) { - // we only need to handle to 1-simplex and 2-simplex - if (simplex.length == 2) { - // 1-simplex - let a = simplex[1], - b = simplex[0], - AO = minus(simplex[1]), - AB = sub(b, a); - // AO is in the same direction as AB - if (dot(AO, AB) > 0) { - // get the vector perpendicular to AB facing O - set(direction, orth(AB, a)); - } else { - set(direction, AO); - // only A remains in the simplex - simplex.shift(); - } - } else { - // 2-simplex - let a = simplex[2], // [a, b, c] = simplex - b = simplex[1], - c = simplex[0], - AB = sub(b, a), - AC = sub(c, a), - AO = minus(a), - ACB = orth(AB, AC), // the vector perpendicular to AB facing away from C - ABC = orth(AC, AB); // the vector perpendicular to AC facing away from B - - if (dot(ACB, AO) > 0) { - if (dot(AB, AO) > 0) { - // region 4 - set(direction, ACB); - simplex.shift(); // simplex = [b, a] - } else { - // region 5 - set(direction, AO); - simplex.splice(0, 2); // simplex = [a] - } - } else if (dot(ABC, AO) > 0) { - if (dot(AC, AO) > 0) { - // region 6 - set(direction, ABC); - simplex.splice(1, 1); // simplex = [c, a] - } else { - // region 5 (again) - set(direction, AO); - simplex.splice(0, 2); // simplex = [a] - } - } // region 7 - else return true; - } - return false; -} - -/** - * @type {(v: number[]) => number[]} - */ -function minus(v) { - return [-v[0], -v[1]]; -} - -/** - * @type {(v1: number[], v2: number[]) => number[]} - */ -function sub(v1, v2) { - return [v1[0] - v2[0], v1[1] - v2[1]]; -} - -/** - * @type {(v1: number[], v2: number[]) => number} - */ -function dot(v1, v2) { - return v1[0] * v2[0] + v1[1] * v2[1]; -} - -/** - * @type {(v1: number[], v2: number[]) => number[]} - */ -function orth(v, from) { - var o = [-v[1], v[0]]; - return dot(o, minus(from)) < 0 ? minus(o) : o; -} - -/** - * @typedef {{ - * list: number[][], - * minX: number, - * minY: number, - * maxX: number, - * maxY: number - * }} Point - */ - -/** - * @typedef {{ - * list: Point[], - * minX: number, - * minY: number, - * maxX: number, - * maxY: number - * }} Points - */ - -/** - * @type {(pathData: PathDataItem[]) => Points} - */ -function gatherPoints(pathData) { - /** - * @type {Points} - */ - const points = { list: [], minX: 0, minY: 0, maxX: 0, maxY: 0 }; - - // Writes data about the extreme points on each axle - /** - * @type {(path: Point, point: number[]) => void} - */ - const addPoint = (path, point) => { - if (!path.list.length || point[1] > path.list[path.maxY][1]) { - path.maxY = path.list.length; - points.maxY = points.list.length - ? Math.max(point[1], points.maxY) - : point[1]; - } - if (!path.list.length || point[0] > path.list[path.maxX][0]) { - path.maxX = path.list.length; - points.maxX = points.list.length - ? Math.max(point[0], points.maxX) - : point[0]; - } - if (!path.list.length || point[1] < path.list[path.minY][1]) { - path.minY = path.list.length; - points.minY = points.list.length - ? Math.min(point[1], points.minY) - : point[1]; - } - if (!path.list.length || point[0] < path.list[path.minX][0]) { - path.minX = path.list.length; - points.minX = points.list.length - ? Math.min(point[0], points.minX) - : point[0]; - } - path.list.push(point); - }; - - for (let i = 0; i < pathData.length; i += 1) { - const pathDataItem = pathData[i]; - let subPath = - points.list.length === 0 - ? { list: [], minX: 0, minY: 0, maxX: 0, maxY: 0 } - : points.list[points.list.length - 1]; - let prev = i === 0 ? null : pathData[i - 1]; - let basePoint = - subPath.list.length === 0 ? null : subPath.list[subPath.list.length - 1]; - let data = pathDataItem.args; - let ctrlPoint = basePoint; - - /** - * @type {(n: number, i: number) => number} - * TODO fix null hack - */ - const toAbsolute = (n, i) => n + (basePoint == null ? 0 : basePoint[i % 2]); - - switch (pathDataItem.command) { - case 'M': - subPath = { list: [], minX: 0, minY: 0, maxX: 0, maxY: 0 }; - points.list.push(subPath); - break; - - case 'H': - if (basePoint != null) { - addPoint(subPath, [data[0], basePoint[1]]); - } - break; - - case 'V': - if (basePoint != null) { - addPoint(subPath, [basePoint[0], data[0]]); - } - break; - - case 'Q': - addPoint(subPath, data.slice(0, 2)); - prevCtrlPoint = [data[2] - data[0], data[3] - data[1]]; // Save control point for shorthand - break; - - case 'T': - if ( - basePoint != null && - prev != null && - (prev.command == 'Q' || prev.command == 'T') - ) { - ctrlPoint = [ - basePoint[0] + prevCtrlPoint[0], - basePoint[1] + prevCtrlPoint[1], - ]; - addPoint(subPath, ctrlPoint); - prevCtrlPoint = [data[0] - ctrlPoint[0], data[1] - ctrlPoint[1]]; - } - break; - - case 'C': - if (basePoint != null) { - // Approximate quibic Bezier curve with middle points between control points - addPoint(subPath, [ - 0.5 * (basePoint[0] + data[0]), - 0.5 * (basePoint[1] + data[1]), - ]); - } - addPoint(subPath, [ - 0.5 * (data[0] + data[2]), - 0.5 * (data[1] + data[3]), - ]); - addPoint(subPath, [ - 0.5 * (data[2] + data[4]), - 0.5 * (data[3] + data[5]), - ]); - prevCtrlPoint = [data[4] - data[2], data[5] - data[3]]; // Save control point for shorthand - break; - - case 'S': - if ( - basePoint != null && - prev != null && - (prev.command == 'C' || prev.command == 'S') - ) { - addPoint(subPath, [ - basePoint[0] + 0.5 * prevCtrlPoint[0], - basePoint[1] + 0.5 * prevCtrlPoint[1], - ]); - ctrlPoint = [ - basePoint[0] + prevCtrlPoint[0], - basePoint[1] + prevCtrlPoint[1], - ]; - } - if (ctrlPoint != null) { - addPoint(subPath, [ - 0.5 * (ctrlPoint[0] + data[0]), - 0.5 * (ctrlPoint[1] + data[1]), - ]); - } - addPoint(subPath, [ - 0.5 * (data[0] + data[2]), - 0.5 * (data[1] + data[3]), - ]); - prevCtrlPoint = [data[2] - data[0], data[3] - data[1]]; - break; - - case 'A': - if (basePoint != null) { - // Convert the arc to bezier curves and use the same approximation - // @ts-ignore no idea what's going on here - var curves = a2c.apply(0, basePoint.concat(data)); - for ( - var cData; - (cData = curves.splice(0, 6).map(toAbsolute)).length; - - ) { - if (basePoint != null) { - addPoint(subPath, [ - 0.5 * (basePoint[0] + cData[0]), - 0.5 * (basePoint[1] + cData[1]), - ]); - } - addPoint(subPath, [ - 0.5 * (cData[0] + cData[2]), - 0.5 * (cData[1] + cData[3]), - ]); - addPoint(subPath, [ - 0.5 * (cData[2] + cData[4]), - 0.5 * (cData[3] + cData[5]), - ]); - if (curves.length) addPoint(subPath, (basePoint = cData.slice(-2))); - } - } - break; - } - - // Save final command coordinates - if (data.length >= 2) addPoint(subPath, data.slice(-2)); - } - - return points; -} - -/** - * Forms a convex hull from set of points of every subpath using monotone chain convex hull algorithm. - * https://en.wikibooks.org/wiki/Algorithm_Implementation/Geometry/Convex_hull/Monotone_chain - * - * @type {(points: Point) => Point} - */ -function convexHull(points) { - points.list.sort(function (a, b) { - return a[0] == b[0] ? a[1] - b[1] : a[0] - b[0]; - }); - - var lower = [], - minY = 0, - bottom = 0; - for (let i = 0; i < points.list.length; i++) { - while ( - lower.length >= 2 && - cross(lower[lower.length - 2], lower[lower.length - 1], points.list[i]) <= - 0 - ) { - lower.pop(); - } - if (points.list[i][1] < points.list[minY][1]) { - minY = i; - bottom = lower.length; - } - lower.push(points.list[i]); - } - - var upper = [], - maxY = points.list.length - 1, - top = 0; - for (let i = points.list.length; i--; ) { - while ( - upper.length >= 2 && - cross(upper[upper.length - 2], upper[upper.length - 1], points.list[i]) <= - 0 - ) { - upper.pop(); - } - if (points.list[i][1] > points.list[maxY][1]) { - maxY = i; - top = upper.length; - } - upper.push(points.list[i]); - } - - // last points are equal to starting points of the other part - upper.pop(); - lower.pop(); - - const hullList = lower.concat(upper); - - /** - * @type {Point} - */ - const hull = { - list: hullList, - minX: 0, // by sorting - maxX: lower.length, - minY: bottom, - maxY: (lower.length + top) % hullList.length, - }; - - return hull; -} - -/** - * @type {(o: number[], a: number[], b: number[]) => number} - */ -function cross(o, a, b) { - return (a[0] - o[0]) * (b[1] - o[1]) - (a[1] - o[1]) * (b[0] - o[0]); -} - -/** - * Based on code from Snap.svg (Apache 2 license). http://snapsvg.io/ - * Thanks to Dmitry Baranovskiy for his great work! - * - * @type {( - * x1: number, - * y1: number, - * rx: number, - * ry: number, - * angle: number, - * large_arc_flag: number, - * sweep_flag: number, - * x2: number, - * y2: number, - * recursive: number[] - * ) => number[]} - */ -const a2c = ( - x1, - y1, - rx, - ry, - angle, - large_arc_flag, - sweep_flag, - x2, - y2, - recursive, -) => { - // for more information of where this Math came from visit: - // https://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes - const _120 = (Math.PI * 120) / 180; - const rad = (Math.PI / 180) * (+angle || 0); - /** - * @type {number[]} - */ - let res = []; - /** - * @type {(x: number, y: number, rad: number) => number} - */ - const rotateX = (x, y, rad) => { - return x * Math.cos(rad) - y * Math.sin(rad); - }; - /** - * @type {(x: number, y: number, rad: number) => number} - */ - const rotateY = (x, y, rad) => { - return x * Math.sin(rad) + y * Math.cos(rad); - }; - if (!recursive) { - x1 = rotateX(x1, y1, -rad); - y1 = rotateY(x1, y1, -rad); - x2 = rotateX(x2, y2, -rad); - y2 = rotateY(x2, y2, -rad); - var x = (x1 - x2) / 2, - y = (y1 - y2) / 2; - var h = (x * x) / (rx * rx) + (y * y) / (ry * ry); - if (h > 1) { - h = Math.sqrt(h); - rx = h * rx; - ry = h * ry; - } - var rx2 = rx * rx; - var ry2 = ry * ry; - var k = - (large_arc_flag == sweep_flag ? -1 : 1) * - Math.sqrt( - Math.abs( - (rx2 * ry2 - rx2 * y * y - ry2 * x * x) / (rx2 * y * y + ry2 * x * x), - ), - ); - var cx = (k * rx * y) / ry + (x1 + x2) / 2; - var cy = (k * -ry * x) / rx + (y1 + y2) / 2; - var f1 = Math.asin(Number(((y1 - cy) / ry).toFixed(9))); - var f2 = Math.asin(Number(((y2 - cy) / ry).toFixed(9))); - - f1 = x1 < cx ? Math.PI - f1 : f1; - f2 = x2 < cx ? Math.PI - f2 : f2; - f1 < 0 && (f1 = Math.PI * 2 + f1); - f2 < 0 && (f2 = Math.PI * 2 + f2); - if (sweep_flag && f1 > f2) { - f1 = f1 - Math.PI * 2; - } - if (!sweep_flag && f2 > f1) { - f2 = f2 - Math.PI * 2; - } - } else { - f1 = recursive[0]; - f2 = recursive[1]; - cx = recursive[2]; - cy = recursive[3]; - } - var df = f2 - f1; - if (Math.abs(df) > _120) { - var f2old = f2, - x2old = x2, - y2old = y2; - f2 = f1 + _120 * (sweep_flag && f2 > f1 ? 1 : -1); - x2 = cx + rx * Math.cos(f2); - y2 = cy + ry * Math.sin(f2); - res = a2c(x2, y2, rx, ry, angle, 0, sweep_flag, x2old, y2old, [ - f2, - f2old, - cx, - cy, - ]); - } - df = f2 - f1; - var c1 = Math.cos(f1), - s1 = Math.sin(f1), - c2 = Math.cos(f2), - s2 = Math.sin(f2), - t = Math.tan(df / 4), - hx = (4 / 3) * rx * t, - hy = (4 / 3) * ry * t, - m = [ - -hx * s1, - hy * c1, - x2 + hx * s2 - x1, - y2 - hy * c2 - y1, - x2 - x1, - y2 - y1, - ]; - if (recursive) { - return m.concat(res); - } else { - res = m.concat(res); - var newres = []; - for (var i = 0, n = res.length; i < n; i++) { - newres[i] = - i % 2 - ? rotateY(res[i - 1], res[i], rad) - : rotateX(res[i], res[i + 1], rad); - } - return newres; - } -}; diff --git a/node_modules/svgo/plugins/_transforms.js b/node_modules/svgo/plugins/_transforms.js deleted file mode 100644 index 1ab6d648..00000000 --- a/node_modules/svgo/plugins/_transforms.js +++ /dev/null @@ -1,410 +0,0 @@ -'use strict'; - -const { toFixed } = require('../lib/svgo/tools'); - -/** - * @typedef {{ name: string, data: number[] }} TransformItem - * @typedef {{ - * convertToShorts: boolean, - * floatPrecision: number, - * transformPrecision: number, - * matrixToTransform: boolean, - * shortTranslate: boolean, - * shortScale: boolean, - * shortRotate: boolean, - * removeUseless: boolean, - * collapseIntoOne: boolean, - * leadingZero: boolean, - * negativeExtraSpace: boolean, - * }} TransformParams - */ - -const transformTypes = new Set([ - 'matrix', - 'rotate', - 'scale', - 'skewX', - 'skewY', - 'translate', -]); - -const regTransformSplit = - /\s*(matrix|translate|scale|rotate|skewX|skewY)\s*\(\s*(.+?)\s*\)[\s,]*/; -const regNumericValues = /[-+]?(?:\d*\.\d+|\d+\.?)(?:[eE][-+]?\d+)?/g; - -/** - * Convert transform string to JS representation. - * - * @param {string} transformString - * @returns {TransformItem[]} Object representation of transform, or an empty array if it was malformed. - */ -exports.transform2js = (transformString) => { - /** @type {TransformItem[]} */ - const transforms = []; - /** @type {?TransformItem} */ - let currentTransform = null; - - // split value into ['', 'translate', '10 50', '', 'scale', '2', '', 'rotate', '-45', ''] - for (const item of transformString.split(regTransformSplit)) { - if (!item) { - continue; - } - - if (transformTypes.has(item)) { - currentTransform = { name: item, data: [] }; - transforms.push(currentTransform); - } else { - let num; - // then split it into [10, 50] and collect as context.data - while ((num = regNumericValues.exec(item))) { - num = Number(num); - if (currentTransform != null) { - currentTransform.data.push(num); - } - } - } - } - - return currentTransform == null || currentTransform.data.length == 0 - ? [] - : transforms; -}; - -/** - * Multiply transforms into one. - * - * @param {TransformItem[]} transforms - * @returns {TransformItem} - */ -exports.transformsMultiply = (transforms) => { - const matrixData = transforms.map((transform) => { - if (transform.name === 'matrix') { - return transform.data; - } - return transformToMatrix(transform); - }); - - const matrixTransform = { - name: 'matrix', - data: - matrixData.length > 0 ? matrixData.reduce(multiplyTransformMatrices) : [], - }; - - return matrixTransform; -}; - -/** - * Math utilities in radians. - */ -const mth = { - /** - * @param {number} deg - * @returns {number} - */ - rad: (deg) => { - return (deg * Math.PI) / 180; - }, - - /** - * @param {number} rad - * @returns {number} - */ - deg: (rad) => { - return (rad * 180) / Math.PI; - }, - - /** - * @param {number} deg - * @returns {number} - */ - cos: (deg) => { - return Math.cos(mth.rad(deg)); - }, - - /** - * @param {number} val - * @param {number} floatPrecision - * @returns {number} - */ - acos: (val, floatPrecision) => { - return toFixed(mth.deg(Math.acos(val)), floatPrecision); - }, - - /** - * @param {number} deg - * @returns {number} - */ - sin: (deg) => { - return Math.sin(mth.rad(deg)); - }, - - /** - * @param {number} val - * @param {number} floatPrecision - * @returns {number} - */ - asin: (val, floatPrecision) => { - return toFixed(mth.deg(Math.asin(val)), floatPrecision); - }, - - /** - * @param {number} deg - * @returns {number} - */ - tan: (deg) => { - return Math.tan(mth.rad(deg)); - }, - - /** - * @param {number} val - * @param {number} floatPrecision - * @returns {number} - */ - atan: (val, floatPrecision) => { - return toFixed(mth.deg(Math.atan(val)), floatPrecision); - }, -}; - -/** - * Decompose matrix into simple transforms. - * - * @param {TransformItem} transform - * @param {TransformParams} params - * @returns {TransformItem[]} - * @see https://frederic-wang.fr/decomposition-of-2d-transform-matrices.html - */ -exports.matrixToTransform = (transform, params) => { - const floatPrecision = params.floatPrecision; - const data = transform.data; - const transforms = []; - - // [..., ..., ..., ..., tx, ty] → translate(tx, ty) - if (data[4] || data[5]) { - transforms.push({ - name: 'translate', - data: data.slice(4, data[5] ? 6 : 5), - }); - } - - let sx = toFixed(Math.hypot(data[0], data[1]), params.transformPrecision); - let sy = toFixed( - (data[0] * data[3] - data[1] * data[2]) / sx, - params.transformPrecision, - ); - const colsSum = data[0] * data[2] + data[1] * data[3]; - const rowsSum = data[0] * data[1] + data[2] * data[3]; - const scaleBefore = rowsSum !== 0 || sx === sy; - - // [sx, 0, tan(a)·sy, sy, 0, 0] → skewX(a)·scale(sx, sy) - if (!data[1] && data[2]) { - transforms.push({ - name: 'skewX', - data: [mth.atan(data[2] / sy, floatPrecision)], - }); - - // [sx, sx·tan(a), 0, sy, 0, 0] → skewY(a)·scale(sx, sy) - } else if (data[1] && !data[2]) { - transforms.push({ - name: 'skewY', - data: [mth.atan(data[1] / data[0], floatPrecision)], - }); - sx = data[0]; - sy = data[3]; - - // [sx·cos(a), sx·sin(a), sy·-sin(a), sy·cos(a), x, y] → rotate(a[, cx, cy])·(scale or skewX) or - // [sx·cos(a), sy·sin(a), sx·-sin(a), sy·cos(a), x, y] → scale(sx, sy)·rotate(a[, cx, cy]) (if !scaleBefore) - } else if (!colsSum || (sx === 1 && sy === 1) || !scaleBefore) { - if (!scaleBefore) { - sx = Math.hypot(data[0], data[2]); - sy = Math.hypot(data[1], data[3]); - - if (toFixed(data[0], params.transformPrecision) < 0) { - sx = -sx; - } - - if ( - data[3] < 0 || - (Math.sign(data[1]) === Math.sign(data[2]) && - toFixed(data[3], params.transformPrecision) === 0) - ) { - sy = -sy; - } - - transforms.push({ name: 'scale', data: [sx, sy] }); - } - const angle = Math.min(Math.max(-1, data[0] / sx), 1); - const rotate = [ - mth.acos(angle, floatPrecision) * - ((scaleBefore ? 1 : sy) * data[1] < 0 ? -1 : 1), - ]; - - if (rotate[0]) { - transforms.push({ name: 'rotate', data: rotate }); - } - - if (rowsSum && colsSum) - transforms.push({ - name: 'skewX', - data: [mth.atan(colsSum / (sx * sx), floatPrecision)], - }); - - // rotate(a, cx, cy) can consume translate() within optional arguments cx, cy (rotation point) - if (rotate[0] && (data[4] || data[5])) { - transforms.shift(); - const oneOverCos = 1 - data[0] / sx; - const sin = data[1] / (scaleBefore ? sx : sy); - const x = data[4] * (scaleBefore ? 1 : sy); - const y = data[5] * (scaleBefore ? 1 : sx); - const denom = (oneOverCos ** 2 + sin ** 2) * (scaleBefore ? 1 : sx * sy); - rotate.push( - (oneOverCos * x - sin * y) / denom, - (oneOverCos * y + sin * x) / denom, - ); - } - - // Too many transformations, return original matrix if it isn't just a scale/translate - } else if (data[1] || data[2]) { - return [transform]; - } - - if ((scaleBefore && (sx != 1 || sy != 1)) || !transforms.length) { - transforms.push({ - name: 'scale', - data: sx == sy ? [sx] : [sx, sy], - }); - } - - return transforms; -}; - -/** - * Convert transform to the matrix data. - * - * @type {(transform: TransformItem) => number[] } - */ -const transformToMatrix = (transform) => { - if (transform.name === 'matrix') { - return transform.data; - } - switch (transform.name) { - case 'translate': - // [1, 0, 0, 1, tx, ty] - return [1, 0, 0, 1, transform.data[0], transform.data[1] || 0]; - case 'scale': - // [sx, 0, 0, sy, 0, 0] - return [ - transform.data[0], - 0, - 0, - transform.data[1] || transform.data[0], - 0, - 0, - ]; - case 'rotate': - // [cos(a), sin(a), -sin(a), cos(a), x, y] - var cos = mth.cos(transform.data[0]), - sin = mth.sin(transform.data[0]), - cx = transform.data[1] || 0, - cy = transform.data[2] || 0; - return [ - cos, - sin, - -sin, - cos, - (1 - cos) * cx + sin * cy, - (1 - cos) * cy - sin * cx, - ]; - case 'skewX': - // [1, 0, tan(a), 1, 0, 0] - return [1, 0, mth.tan(transform.data[0]), 1, 0, 0]; - case 'skewY': - // [1, tan(a), 0, 1, 0, 0] - return [1, mth.tan(transform.data[0]), 0, 1, 0, 0]; - default: - throw Error(`Unknown transform ${transform.name}`); - } -}; - -/** - * Applies transformation to an arc. To do so, we represent ellipse as a matrix, multiply it - * by the transformation matrix and use a singular value decomposition to represent in a form - * rotate(θ)·scale(a b)·rotate(φ). This gives us new ellipse params a, b and θ. - * SVD is being done with the formulae provided by Wolffram|Alpha (svd {{m0, m2}, {m1, m3}}) - * - * @type {( - * cursor: [x: number, y: number], - * arc: number[], - * transform: number[] - * ) => number[]} - */ -exports.transformArc = (cursor, arc, transform) => { - const x = arc[5] - cursor[0]; - const y = arc[6] - cursor[1]; - let a = arc[0]; - let b = arc[1]; - const rot = (arc[2] * Math.PI) / 180; - const cos = Math.cos(rot); - const sin = Math.sin(rot); - // skip if radius is 0 - if (a > 0 && b > 0) { - let h = - Math.pow(x * cos + y * sin, 2) / (4 * a * a) + - Math.pow(y * cos - x * sin, 2) / (4 * b * b); - if (h > 1) { - h = Math.sqrt(h); - a *= h; - b *= h; - } - } - const ellipse = [a * cos, a * sin, -b * sin, b * cos, 0, 0]; - const m = multiplyTransformMatrices(transform, ellipse); - // Decompose the new ellipse matrix - const lastCol = m[2] * m[2] + m[3] * m[3]; - const squareSum = m[0] * m[0] + m[1] * m[1] + lastCol; - const root = - Math.hypot(m[0] - m[3], m[1] + m[2]) * Math.hypot(m[0] + m[3], m[1] - m[2]); - - if (!root) { - // circle - arc[0] = arc[1] = Math.sqrt(squareSum / 2); - arc[2] = 0; - } else { - const majorAxisSqr = (squareSum + root) / 2; - const minorAxisSqr = (squareSum - root) / 2; - const major = Math.abs(majorAxisSqr - lastCol) > 1e-6; - const sub = (major ? majorAxisSqr : minorAxisSqr) - lastCol; - const rowsSum = m[0] * m[2] + m[1] * m[3]; - const term1 = m[0] * sub + m[2] * rowsSum; - const term2 = m[1] * sub + m[3] * rowsSum; - arc[0] = Math.sqrt(majorAxisSqr); - arc[1] = Math.sqrt(minorAxisSqr); - arc[2] = - (((major ? term2 < 0 : term1 > 0) ? -1 : 1) * - Math.acos((major ? term1 : term2) / Math.hypot(term1, term2)) * - 180) / - Math.PI; - } - - if (transform[0] < 0 !== transform[3] < 0) { - // Flip the sweep flag if coordinates are being flipped horizontally XOR vertically - arc[4] = 1 - arc[4]; - } - - return arc; -}; - -/** - * Multiply transformation matrices. - * - * @type {(a: number[], b: number[]) => number[]} - */ -const multiplyTransformMatrices = (a, b) => { - return [ - a[0] * b[0] + a[2] * b[1], - a[1] * b[0] + a[3] * b[1], - a[0] * b[2] + a[2] * b[3], - a[1] * b[2] + a[3] * b[3], - a[0] * b[4] + a[2] * b[5] + a[4], - a[1] * b[4] + a[3] * b[5] + a[5], - ]; -}; diff --git a/node_modules/svgo/plugins/addAttributesToSVGElement.js b/node_modules/svgo/plugins/addAttributesToSVGElement.js deleted file mode 100644 index 845cb056..00000000 --- a/node_modules/svgo/plugins/addAttributesToSVGElement.js +++ /dev/null @@ -1,82 +0,0 @@ -'use strict'; - -exports.name = 'addAttributesToSVGElement'; -exports.description = 'adds attributes to an outer <svg> element'; - -var ENOCLS = `Error in plugin "addAttributesToSVGElement": absent parameters. -It should have a list of "attributes" or one "attribute". -Config example: - -plugins: [ - { - name: 'addAttributesToSVGElement', - params: { - attribute: "mySvg" - } - } -] - -plugins: [ - { - name: 'addAttributesToSVGElement', - params: { - attributes: ["mySvg", "size-big"] - } - } -] - -plugins: [ - { - name: 'addAttributesToSVGElement', - params: { - attributes: [ - { - focusable: false - }, - { - 'data-image': icon - } - ] - } - } -] -`; - -/** - * Add attributes to an outer <svg> element. Example config: - * - * @author April Arcus - * - * @type {import('./plugins-types').Plugin<'addAttributesToSVGElement'>} - */ -exports.fn = (root, params) => { - if (!Array.isArray(params.attributes) && !params.attribute) { - console.error(ENOCLS); - return null; - } - const attributes = params.attributes || [params.attribute]; - return { - element: { - enter: (node, parentNode) => { - if (node.name === 'svg' && parentNode.type === 'root') { - for (const attribute of attributes) { - if (typeof attribute === 'string') { - if (node.attributes[attribute] == null) { - // @ts-ignore disallow explicit nullable attribute value - node.attributes[attribute] = undefined; - } - } - if (typeof attribute === 'object') { - for (const key of Object.keys(attribute)) { - if (node.attributes[key] == null) { - // @ts-ignore disallow explicit nullable attribute value - node.attributes[key] = attribute[key]; - } - } - } - } - } - }, - }, - }; -}; diff --git a/node_modules/svgo/plugins/addClassesToSVGElement.js b/node_modules/svgo/plugins/addClassesToSVGElement.js deleted file mode 100644 index 1e773d85..00000000 --- a/node_modules/svgo/plugins/addClassesToSVGElement.js +++ /dev/null @@ -1,82 +0,0 @@ -'use strict'; - -exports.name = 'addClassesToSVGElement'; -exports.description = 'adds classnames to an outer <svg> element'; - -var ENOCLS = `Error in plugin "addClassesToSVGElement": absent parameters. -It should have a list of classes in "classNames" or one "className". -Config example: - -plugins: [ - { - name: "addClassesToSVGElement", - params: { - className: "mySvg" - } - } -] - -plugins: [ - { - name: "addClassesToSVGElement", - params: { - classNames: ["mySvg", "size-big"] - } - } -] -`; - -/** - * Add classnames to an outer <svg> element. Example config: - * - * plugins: [ - * { - * name: "addClassesToSVGElement", - * params: { - * className: "mySvg" - * } - * } - * ] - * - * plugins: [ - * { - * name: "addClassesToSVGElement", - * params: { - * classNames: ["mySvg", "size-big"] - * } - * } - * ] - * - * @author April Arcus - * - * @type {import('./plugins-types').Plugin<'addClassesToSVGElement'>} - */ -exports.fn = (root, params) => { - if ( - !(Array.isArray(params.classNames) && params.classNames.some(String)) && - !params.className - ) { - console.error(ENOCLS); - return null; - } - const classNames = params.classNames || [params.className]; - return { - element: { - enter: (node, parentNode) => { - if (node.name === 'svg' && parentNode.type === 'root') { - const classList = new Set( - node.attributes.class == null - ? null - : node.attributes.class.split(' '), - ); - for (const className of classNames) { - if (className != null) { - classList.add(className); - } - } - node.attributes.class = Array.from(classList).join(' '); - } - }, - }, - }; -}; diff --git a/node_modules/svgo/plugins/applyTransforms.js b/node_modules/svgo/plugins/applyTransforms.js deleted file mode 100644 index ae492d54..00000000 --- a/node_modules/svgo/plugins/applyTransforms.js +++ /dev/null @@ -1,388 +0,0 @@ -'use strict'; - -/** - * @typedef {import('../lib/types').PathDataItem} PathDataItem - * @typedef {import('../lib/types').XastElement} XastElement - */ - -const { collectStylesheet, computeStyle } = require('../lib/style.js'); -const { - transformsMultiply, - transform2js, - transformArc, -} = require('./_transforms.js'); -const { path2js } = require('./_path.js'); -const { - removeLeadingZero, - includesUrlReference, -} = require('../lib/svgo/tools.js'); -const { referencesProps, attrsGroupsDefaults } = require('./_collections.js'); - -/** - * @typedef {PathDataItem[]} PathData - * @typedef {number[]} Matrix - */ - -const regNumericValues = /[-+]?(\d*\.\d+|\d+\.?)(?:[eE][-+]?\d+)?/g; - -/** - * Apply transformation(s) to the Path data. - * - * @type {import('../lib/types').Plugin<{ - * transformPrecision: number, - * applyTransformsStroked: boolean, - * }>} - */ -const applyTransforms = (root, params) => { - const stylesheet = collectStylesheet(root); - return { - element: { - enter: (node) => { - if (node.attributes.d == null) { - return; - } - - // stroke and stroke-width can be redefined with <use> - if (node.attributes.id != null) { - return; - } - - // if there are no 'stroke' attr and references to other objects such as - // gradients or clip-path which are also subjects to transform. - if ( - node.attributes.transform == null || - node.attributes.transform === '' || - // styles are not considered when applying transform - // can be fixed properly with new style engine - node.attributes.style != null || - Object.entries(node.attributes).some( - ([name, value]) => - referencesProps.has(name) && includesUrlReference(value), - ) - ) { - return; - } - - const computedStyle = computeStyle(stylesheet, node); - const transformStyle = computedStyle.transform; - - // Transform overridden in <style> tag which is not considered - if ( - transformStyle.type === 'static' && - transformStyle.value !== node.attributes.transform - ) { - return; - } - - const matrix = transformsMultiply( - transform2js(node.attributes.transform), - ); - - const stroke = - computedStyle.stroke?.type === 'static' - ? computedStyle.stroke.value - : null; - - const strokeWidth = - computedStyle['stroke-width']?.type === 'static' - ? computedStyle['stroke-width'].value - : null; - const transformPrecision = params.transformPrecision; - - if ( - computedStyle.stroke?.type === 'dynamic' || - computedStyle['stroke-width']?.type === 'dynamic' - ) { - return; - } - - const scale = Number( - Math.sqrt( - matrix.data[0] * matrix.data[0] + matrix.data[1] * matrix.data[1], - ).toFixed(transformPrecision), - ); - - if (stroke && stroke != 'none') { - if (!params.applyTransformsStroked) { - return; - } - - // stroke cannot be transformed with different vertical and horizontal scale or skew - if ( - (matrix.data[0] !== matrix.data[3] || - matrix.data[1] !== -matrix.data[2]) && - (matrix.data[0] !== -matrix.data[3] || - matrix.data[1] !== matrix.data[2]) - ) { - return; - } - - // apply transform to stroke-width, stroke-dashoffset and stroke-dasharray - if (scale !== 1) { - if (node.attributes['vector-effect'] !== 'non-scaling-stroke') { - node.attributes['stroke-width'] = ( - strokeWidth || attrsGroupsDefaults.presentation['stroke-width'] - ) - .trim() - .replace(regNumericValues, (num) => - removeLeadingZero(Number(num) * scale), - ); - - if (node.attributes['stroke-dashoffset'] != null) { - node.attributes['stroke-dashoffset'] = node.attributes[ - 'stroke-dashoffset' - ] - .trim() - .replace(regNumericValues, (num) => - removeLeadingZero(Number(num) * scale), - ); - } - - if (node.attributes['stroke-dasharray'] != null) { - node.attributes['stroke-dasharray'] = node.attributes[ - 'stroke-dasharray' - ] - .trim() - .replace(regNumericValues, (num) => - removeLeadingZero(Number(num) * scale), - ); - } - } - } - } - - const pathData = path2js(node); - applyMatrixToPathData(pathData, matrix.data); - - // remove transform attr - delete node.attributes.transform; - }, - }, - }; -}; -exports.applyTransforms = applyTransforms; - -/** - * @type {(matrix: Matrix, x: number, y: number) => [number, number]} - */ -const transformAbsolutePoint = (matrix, x, y) => { - const newX = matrix[0] * x + matrix[2] * y + matrix[4]; - const newY = matrix[1] * x + matrix[3] * y + matrix[5]; - return [newX, newY]; -}; - -/** - * @type {(matrix: Matrix, x: number, y: number) => [number, number]} - */ -const transformRelativePoint = (matrix, x, y) => { - const newX = matrix[0] * x + matrix[2] * y; - const newY = matrix[1] * x + matrix[3] * y; - return [newX, newY]; -}; - -/** - * @type {(pathData: PathData, matrix: Matrix) => void} - */ -const applyMatrixToPathData = (pathData, matrix) => { - /** - * @type {[number, number]} - */ - const start = [0, 0]; - /** - * @type {[number, number]} - */ - const cursor = [0, 0]; - - for (const pathItem of pathData) { - let { command, args } = pathItem; - - // moveto (x y) - if (command === 'M') { - cursor[0] = args[0]; - cursor[1] = args[1]; - start[0] = cursor[0]; - start[1] = cursor[1]; - const [x, y] = transformAbsolutePoint(matrix, args[0], args[1]); - args[0] = x; - args[1] = y; - } - if (command === 'm') { - cursor[0] += args[0]; - cursor[1] += args[1]; - start[0] = cursor[0]; - start[1] = cursor[1]; - const [x, y] = transformRelativePoint(matrix, args[0], args[1]); - args[0] = x; - args[1] = y; - } - - // horizontal lineto (x) - // convert to lineto to handle two-dimentional transforms - if (command === 'H') { - command = 'L'; - args = [args[0], cursor[1]]; - } - if (command === 'h') { - command = 'l'; - args = [args[0], 0]; - } - - // vertical lineto (y) - // convert to lineto to handle two-dimentional transforms - if (command === 'V') { - command = 'L'; - args = [cursor[0], args[0]]; - } - if (command === 'v') { - command = 'l'; - args = [0, args[0]]; - } - - // lineto (x y) - if (command === 'L') { - cursor[0] = args[0]; - cursor[1] = args[1]; - const [x, y] = transformAbsolutePoint(matrix, args[0], args[1]); - args[0] = x; - args[1] = y; - } - if (command === 'l') { - cursor[0] += args[0]; - cursor[1] += args[1]; - const [x, y] = transformRelativePoint(matrix, args[0], args[1]); - args[0] = x; - args[1] = y; - } - - // curveto (x1 y1 x2 y2 x y) - if (command === 'C') { - cursor[0] = args[4]; - cursor[1] = args[5]; - const [x1, y1] = transformAbsolutePoint(matrix, args[0], args[1]); - const [x2, y2] = transformAbsolutePoint(matrix, args[2], args[3]); - const [x, y] = transformAbsolutePoint(matrix, args[4], args[5]); - args[0] = x1; - args[1] = y1; - args[2] = x2; - args[3] = y2; - args[4] = x; - args[5] = y; - } - if (command === 'c') { - cursor[0] += args[4]; - cursor[1] += args[5]; - const [x1, y1] = transformRelativePoint(matrix, args[0], args[1]); - const [x2, y2] = transformRelativePoint(matrix, args[2], args[3]); - const [x, y] = transformRelativePoint(matrix, args[4], args[5]); - args[0] = x1; - args[1] = y1; - args[2] = x2; - args[3] = y2; - args[4] = x; - args[5] = y; - } - - // smooth curveto (x2 y2 x y) - if (command === 'S') { - cursor[0] = args[2]; - cursor[1] = args[3]; - const [x2, y2] = transformAbsolutePoint(matrix, args[0], args[1]); - const [x, y] = transformAbsolutePoint(matrix, args[2], args[3]); - args[0] = x2; - args[1] = y2; - args[2] = x; - args[3] = y; - } - if (command === 's') { - cursor[0] += args[2]; - cursor[1] += args[3]; - const [x2, y2] = transformRelativePoint(matrix, args[0], args[1]); - const [x, y] = transformRelativePoint(matrix, args[2], args[3]); - args[0] = x2; - args[1] = y2; - args[2] = x; - args[3] = y; - } - - // quadratic Bézier curveto (x1 y1 x y) - if (command === 'Q') { - cursor[0] = args[2]; - cursor[1] = args[3]; - const [x1, y1] = transformAbsolutePoint(matrix, args[0], args[1]); - const [x, y] = transformAbsolutePoint(matrix, args[2], args[3]); - args[0] = x1; - args[1] = y1; - args[2] = x; - args[3] = y; - } - if (command === 'q') { - cursor[0] += args[2]; - cursor[1] += args[3]; - const [x1, y1] = transformRelativePoint(matrix, args[0], args[1]); - const [x, y] = transformRelativePoint(matrix, args[2], args[3]); - args[0] = x1; - args[1] = y1; - args[2] = x; - args[3] = y; - } - - // smooth quadratic Bézier curveto (x y) - if (command === 'T') { - cursor[0] = args[0]; - cursor[1] = args[1]; - const [x, y] = transformAbsolutePoint(matrix, args[0], args[1]); - args[0] = x; - args[1] = y; - } - if (command === 't') { - cursor[0] += args[0]; - cursor[1] += args[1]; - const [x, y] = transformRelativePoint(matrix, args[0], args[1]); - args[0] = x; - args[1] = y; - } - - // elliptical arc (rx ry x-axis-rotation large-arc-flag sweep-flag x y) - if (command === 'A') { - transformArc(cursor, args, matrix); - cursor[0] = args[5]; - cursor[1] = args[6]; - // reduce number of digits in rotation angle - if (Math.abs(args[2]) > 80) { - const a = args[0]; - const rotation = args[2]; - args[0] = args[1]; - args[1] = a; - args[2] = rotation + (rotation > 0 ? -90 : 90); - } - const [x, y] = transformAbsolutePoint(matrix, args[5], args[6]); - args[5] = x; - args[6] = y; - } - if (command === 'a') { - transformArc([0, 0], args, matrix); - cursor[0] += args[5]; - cursor[1] += args[6]; - // reduce number of digits in rotation angle - if (Math.abs(args[2]) > 80) { - const a = args[0]; - const rotation = args[2]; - args[0] = args[1]; - args[1] = a; - args[2] = rotation + (rotation > 0 ? -90 : 90); - } - const [x, y] = transformRelativePoint(matrix, args[5], args[6]); - args[5] = x; - args[6] = y; - } - - // closepath - if (command === 'z' || command === 'Z') { - cursor[0] = start[0]; - cursor[1] = start[1]; - } - - pathItem.command = command; - pathItem.args = args; - } -}; diff --git a/node_modules/svgo/plugins/cleanupAttrs.js b/node_modules/svgo/plugins/cleanupAttrs.js deleted file mode 100644 index 7b4c6b70..00000000 --- a/node_modules/svgo/plugins/cleanupAttrs.js +++ /dev/null @@ -1,49 +0,0 @@ -'use strict'; - -exports.name = 'cleanupAttrs'; -exports.description = - 'cleanups attributes from newlines, trailing and repeating spaces'; - -const regNewlinesNeedSpace = /(\S)\r?\n(\S)/g; -const regNewlines = /\r?\n/g; -const regSpaces = /\s{2,}/g; - -/** - * Cleanup attributes values from newlines, trailing and repeating spaces. - * - * @author Kir Belevich - * - * @type {import('./plugins-types').Plugin<'cleanupAttrs'>} - */ -exports.fn = (root, params) => { - const { newlines = true, trim = true, spaces = true } = params; - return { - element: { - enter: (node) => { - for (const name of Object.keys(node.attributes)) { - if (newlines) { - // new line which requires a space instead of themself - node.attributes[name] = node.attributes[name].replace( - regNewlinesNeedSpace, - (match, p1, p2) => p1 + ' ' + p2, - ); - // simple new line - node.attributes[name] = node.attributes[name].replace( - regNewlines, - '', - ); - } - if (trim) { - node.attributes[name] = node.attributes[name].trim(); - } - if (spaces) { - node.attributes[name] = node.attributes[name].replace( - regSpaces, - ' ', - ); - } - } - }, - }, - }; -}; diff --git a/node_modules/svgo/plugins/cleanupEnableBackground.js b/node_modules/svgo/plugins/cleanupEnableBackground.js deleted file mode 100644 index 330cfb57..00000000 --- a/node_modules/svgo/plugins/cleanupEnableBackground.js +++ /dev/null @@ -1,165 +0,0 @@ -'use strict'; - -const csstree = require('css-tree'); -const { visit } = require('../lib/xast.js'); - -exports.name = 'cleanupEnableBackground'; -exports.description = - 'remove or cleanup enable-background attribute when possible'; - -const regEnableBackground = - /^new\s0\s0\s([-+]?\d*\.?\d+([eE][-+]?\d+)?)\s([-+]?\d*\.?\d+([eE][-+]?\d+)?)$/; - -/** - * Remove or cleanup enable-background attr which coincides with a width/height box. - * - * @see https://www.w3.org/TR/SVG11/filters.html#EnableBackgroundProperty - * @example - * <svg width="100" height="50" enable-background="new 0 0 100 50"> - * ⬇ - * <svg width="100" height="50"> - * @author Kir Belevich - * @type {import('./plugins-types').Plugin<'cleanupEnableBackground'>} - */ -exports.fn = (root) => { - let hasFilter = false; - - visit(root, { - element: { - enter: (node) => { - if (node.name === 'filter') { - hasFilter = true; - } - }, - }, - }); - - return { - element: { - enter: (node) => { - /** @type {?csstree.CssNode} */ - let newStyle = null; - /** @type {?csstree.ListItem<csstree.CssNode>} */ - let enableBackgroundDeclaration = null; - - if (node.attributes.style != null) { - newStyle = csstree.parse(node.attributes.style, { - context: 'declarationList', - }); - - if (newStyle.type === 'DeclarationList') { - /** @type {csstree.ListItem<csstree.CssNode>[]} */ - const enableBackgroundDeclarations = []; - - csstree.walk(newStyle, (node, nodeItem) => { - if ( - node.type === 'Declaration' && - node.property === 'enable-background' - ) { - enableBackgroundDeclarations.push(nodeItem); - enableBackgroundDeclaration = nodeItem; - } - }); - - for (let i = 0; i < enableBackgroundDeclarations.length - 1; i++) { - newStyle.children.remove(enableBackgroundDeclarations[i]); - } - } - } - - if (!hasFilter) { - delete node.attributes['enable-background']; - - if (newStyle?.type === 'DeclarationList') { - if (enableBackgroundDeclaration) { - newStyle.children.remove(enableBackgroundDeclaration); - } - - if (newStyle.children.isEmpty) { - delete node.attributes.style; - } else { - node.attributes.style = csstree.generate(newStyle); - } - } - - return; - } - - const hasDimensions = - node.attributes.width != null && node.attributes.height != null; - - if ( - (node.name === 'svg' || - node.name === 'mask' || - node.name === 'pattern') && - hasDimensions - ) { - const attrValue = node.attributes['enable-background']; - const attrCleaned = cleanupValue( - attrValue, - node.name, - node.attributes.width, - node.attributes.height, - ); - - if (attrCleaned) { - node.attributes['enable-background'] = attrCleaned; - } else { - delete node.attributes['enable-background']; - } - - if ( - newStyle?.type === 'DeclarationList' && - enableBackgroundDeclaration - ) { - const styleValue = csstree.generate( - // @ts-ignore - enableBackgroundDeclaration.data.value, - ); - const styleCleaned = cleanupValue( - styleValue, - node.name, - node.attributes.width, - node.attributes.height, - ); - - if (styleCleaned) { - // @ts-ignore - enableBackgroundDeclaration.data.value = { - type: 'Raw', - value: styleCleaned, - }; - } else { - newStyle.children.remove(enableBackgroundDeclaration); - } - } - } - - if (newStyle?.type === 'DeclarationList') { - if (newStyle.children.isEmpty) { - delete node.attributes.style; - } else { - node.attributes.style = csstree.generate(newStyle); - } - } - }, - }, - }; -}; - -/** - * @param {string} value Value of a enable-background attribute or style declaration. - * @param {string} nodeName Name of the node the value was assigned to. - * @param {string} width Width of the node the value was assigned to. - * @param {string} height Height of the node the value was assigned to. - * @returns {string | undefined} Cleaned up value, or undefined if it's redundant. - */ -const cleanupValue = (value, nodeName, width, height) => { - const match = regEnableBackground.exec(value); - - if (match != null && width === match[1] && height === match[3]) { - return nodeName === 'svg' ? undefined : 'new'; - } - - return value; -}; diff --git a/node_modules/svgo/plugins/cleanupIds.js b/node_modules/svgo/plugins/cleanupIds.js deleted file mode 100644 index bfabcca5..00000000 --- a/node_modules/svgo/plugins/cleanupIds.js +++ /dev/null @@ -1,265 +0,0 @@ -'use strict'; - -/** - * @typedef {import('../lib/types').XastElement} XastElement - */ - -const { visitSkip } = require('../lib/xast.js'); -const { hasScripts, findReferences } = require('../lib/svgo/tools'); - -exports.name = 'cleanupIds'; -exports.description = 'removes unused IDs and minifies used'; - -const generateIdChars = [ - 'a', - 'b', - 'c', - 'd', - 'e', - 'f', - 'g', - 'h', - 'i', - 'j', - 'k', - 'l', - 'm', - 'n', - 'o', - 'p', - 'q', - 'r', - 's', - 't', - 'u', - 'v', - 'w', - 'x', - 'y', - 'z', - 'A', - 'B', - 'C', - 'D', - 'E', - 'F', - 'G', - 'H', - 'I', - 'J', - 'K', - 'L', - 'M', - 'N', - 'O', - 'P', - 'Q', - 'R', - 'S', - 'T', - 'U', - 'V', - 'W', - 'X', - 'Y', - 'Z', -]; -const maxIdIndex = generateIdChars.length - 1; - -/** - * Check if an ID starts with any one of a list of strings. - * - * @type {(string: string, prefixes: string[]) => boolean} - */ -const hasStringPrefix = (string, prefixes) => { - for (const prefix of prefixes) { - if (string.startsWith(prefix)) { - return true; - } - } - return false; -}; - -/** - * Generate unique minimal ID. - * - * @param {?number[]} currentId - * @returns {number[]} - */ -const generateId = (currentId) => { - if (currentId == null) { - return [0]; - } - currentId[currentId.length - 1] += 1; - for (let i = currentId.length - 1; i > 0; i--) { - if (currentId[i] > maxIdIndex) { - currentId[i] = 0; - if (currentId[i - 1] !== undefined) { - currentId[i - 1]++; - } - } - } - if (currentId[0] > maxIdIndex) { - currentId[0] = 0; - currentId.unshift(0); - } - return currentId; -}; - -/** - * Get string from generated ID array. - * - * @type {(arr: number[]) => string} - */ -const getIdString = (arr) => { - return arr.map((i) => generateIdChars[i]).join(''); -}; - -/** - * Remove unused and minify used IDs - * (only if there are no any <style> or <script>). - * - * @author Kir Belevich - * - * @type {import('./plugins-types').Plugin<'cleanupIds'>} - */ -exports.fn = (_root, params) => { - const { - remove = true, - minify = true, - preserve = [], - preservePrefixes = [], - force = false, - } = params; - const preserveIds = new Set( - Array.isArray(preserve) ? preserve : preserve ? [preserve] : [], - ); - const preserveIdPrefixes = Array.isArray(preservePrefixes) - ? preservePrefixes - : preservePrefixes - ? [preservePrefixes] - : []; - /** - * @type {Map<string, XastElement>} - */ - const nodeById = new Map(); - /** - * @type {Map<string, {element: XastElement, name: string }[]>} - */ - const referencesById = new Map(); - let deoptimized = false; - - return { - element: { - enter: (node) => { - if (!force) { - // deoptimize if style or scripts are present - if ( - (node.name === 'style' && node.children.length !== 0) || - hasScripts(node) - ) { - deoptimized = true; - return; - } - - // avoid removing IDs if the whole SVG consists only of defs - if (node.name === 'svg') { - let hasDefsOnly = true; - for (const child of node.children) { - if (child.type !== 'element' || child.name !== 'defs') { - hasDefsOnly = false; - break; - } - } - if (hasDefsOnly) { - return visitSkip; - } - } - } - - for (const [name, value] of Object.entries(node.attributes)) { - if (name === 'id') { - // collect all ids - const id = value; - if (nodeById.has(id)) { - delete node.attributes.id; // remove repeated id - } else { - nodeById.set(id, node); - } - } else { - const ids = findReferences(name, value); - for (const id of ids) { - let refs = referencesById.get(id); - if (refs == null) { - refs = []; - referencesById.set(id, refs); - } - refs.push({ element: node, name }); - } - } - } - }, - }, - - root: { - exit: () => { - if (deoptimized) { - return; - } - /** - * @param {string} id - * @returns {boolean} - */ - const isIdPreserved = (id) => - preserveIds.has(id) || hasStringPrefix(id, preserveIdPrefixes); - /** @type {?number[]} */ - let currentId = null; - for (const [id, refs] of referencesById) { - const node = nodeById.get(id); - if (node != null) { - // replace referenced IDs with the minified ones - if (minify && isIdPreserved(id) === false) { - /** @type {?string} */ - let currentIdString = null; - do { - currentId = generateId(currentId); - currentIdString = getIdString(currentId); - } while ( - isIdPreserved(currentIdString) || - (referencesById.has(currentIdString) && - nodeById.get(currentIdString) == null) - ); - node.attributes.id = currentIdString; - for (const { element, name } of refs) { - const value = element.attributes[name]; - if (value.includes('#')) { - // replace id in href and url() - element.attributes[name] = value.replace( - `#${encodeURI(id)}`, - `#${currentIdString}`, - ); - } else { - // replace id in begin attribute - element.attributes[name] = value.replace( - `${id}.`, - `${currentIdString}.`, - ); - } - } - } - // keep referenced node - nodeById.delete(id); - } - } - // remove non-referenced IDs attributes from elements - if (remove) { - for (const [id, node] of nodeById) { - if (isIdPreserved(id) === false) { - delete node.attributes.id; - } - } - } - }, - }, - }; -}; diff --git a/node_modules/svgo/plugins/cleanupListOfValues.js b/node_modules/svgo/plugins/cleanupListOfValues.js deleted file mode 100644 index 618fd062..00000000 --- a/node_modules/svgo/plugins/cleanupListOfValues.js +++ /dev/null @@ -1,147 +0,0 @@ -'use strict'; - -const { removeLeadingZero } = require('../lib/svgo/tools.js'); - -exports.name = 'cleanupListOfValues'; -exports.description = 'rounds list of values to the fixed precision'; - -const regNumericValues = - /^([-+]?\d*\.?\d+([eE][-+]?\d+)?)(px|pt|pc|mm|cm|m|in|ft|em|ex|%)?$/; -const regSeparator = /\s+,?\s*|,\s*/; -const absoluteLengths = { - // relative to px - cm: 96 / 2.54, - mm: 96 / 25.4, - in: 96, - pt: 4 / 3, - pc: 16, - px: 1, -}; - -/** - * Round list of values to the fixed precision. - * - * @example - * <svg viewBox="0 0 200.28423 200.28423" enable-background="new 0 0 200.28423 200.28423"> - * ⬇ - * <svg viewBox="0 0 200.284 200.284" enable-background="new 0 0 200.284 200.284"> - * - * <polygon points="208.250977 77.1308594 223.069336 ... "/> - * ⬇ - * <polygon points="208.251 77.131 223.069 ... "/> - * - * @author kiyopikko - * - * @type {import('./plugins-types').Plugin<'cleanupListOfValues'>} - */ -exports.fn = (_root, params) => { - const { - floatPrecision = 3, - leadingZero = true, - defaultPx = true, - convertToPx = true, - } = params; - - /** - * @type {(lists: string) => string} - */ - const roundValues = (lists) => { - const roundedList = []; - - for (const elem of lists.split(regSeparator)) { - const match = elem.match(regNumericValues); - const matchNew = elem.match(/new/); - - // if attribute value matches regNumericValues - if (match) { - // round it to the fixed precision - let num = Number(Number(match[1]).toFixed(floatPrecision)); - /** - * @type {any} - */ - let matchedUnit = match[3] || ''; - /** - * @type{'' | keyof typeof absoluteLengths} - */ - let units = matchedUnit; - - // convert absolute values to pixels - if (convertToPx && units && units in absoluteLengths) { - const pxNum = Number( - (absoluteLengths[units] * Number(match[1])).toFixed(floatPrecision), - ); - - if (pxNum.toString().length < match[0].length) { - num = pxNum; - units = 'px'; - } - } - - // and remove leading zero - let str; - if (leadingZero) { - str = removeLeadingZero(num); - } else { - str = num.toString(); - } - - // remove default 'px' units - if (defaultPx && units === 'px') { - units = ''; - } - - roundedList.push(str + units); - } - // if attribute value is "new"(only enable-background). - else if (matchNew) { - roundedList.push('new'); - } else if (elem) { - roundedList.push(elem); - } - } - - return roundedList.join(' '); - }; - - return { - element: { - enter: (node) => { - if (node.attributes.points != null) { - node.attributes.points = roundValues(node.attributes.points); - } - - if (node.attributes['enable-background'] != null) { - node.attributes['enable-background'] = roundValues( - node.attributes['enable-background'], - ); - } - - if (node.attributes.viewBox != null) { - node.attributes.viewBox = roundValues(node.attributes.viewBox); - } - - if (node.attributes['stroke-dasharray'] != null) { - node.attributes['stroke-dasharray'] = roundValues( - node.attributes['stroke-dasharray'], - ); - } - - if (node.attributes.dx != null) { - node.attributes.dx = roundValues(node.attributes.dx); - } - - if (node.attributes.dy != null) { - node.attributes.dy = roundValues(node.attributes.dy); - } - - if (node.attributes.x != null) { - node.attributes.x = roundValues(node.attributes.x); - } - - if (node.attributes.y != null) { - node.attributes.y = roundValues(node.attributes.y); - } - }, - }, - }; -}; diff --git a/node_modules/svgo/plugins/cleanupNumericValues.js b/node_modules/svgo/plugins/cleanupNumericValues.js deleted file mode 100644 index 9b782c3d..00000000 --- a/node_modules/svgo/plugins/cleanupNumericValues.js +++ /dev/null @@ -1,106 +0,0 @@ -'use strict'; - -const { removeLeadingZero } = require('../lib/svgo/tools'); - -exports.name = 'cleanupNumericValues'; -exports.description = - 'rounds numeric values to the fixed precision, removes default ‘px’ units'; - -const regNumericValues = - /^([-+]?\d*\.?\d+([eE][-+]?\d+)?)(px|pt|pc|mm|cm|m|in|ft|em|ex|%)?$/; - -const absoluteLengths = { - // relative to px - cm: 96 / 2.54, - mm: 96 / 25.4, - in: 96, - pt: 4 / 3, - pc: 16, - px: 1, -}; - -/** - * Round numeric values to the fixed precision, - * remove default 'px' units. - * - * @author Kir Belevich - * - * @type {import('./plugins-types').Plugin<'cleanupNumericValues'>} - */ -exports.fn = (_root, params) => { - const { - floatPrecision = 3, - leadingZero = true, - defaultPx = true, - convertToPx = true, - } = params; - - return { - element: { - enter: (node) => { - if (node.attributes.viewBox != null) { - const nums = node.attributes.viewBox.split(/\s,?\s*|,\s*/g); - node.attributes.viewBox = nums - .map((value) => { - const num = Number(value); - return Number.isNaN(num) - ? value - : Number(num.toFixed(floatPrecision)); - }) - .join(' '); - } - - for (const [name, value] of Object.entries(node.attributes)) { - // The `version` attribute is a text string and cannot be rounded - if (name === 'version') { - continue; - } - - const match = value.match(regNumericValues); - - // if attribute value matches regNumericValues - if (match) { - // round it to the fixed precision - let num = Number(Number(match[1]).toFixed(floatPrecision)); - /** - * @type {any} - */ - let matchedUnit = match[3] || ''; - /** - * @type{'' | keyof typeof absoluteLengths} - */ - let units = matchedUnit; - - // convert absolute values to pixels - if (convertToPx && units !== '' && units in absoluteLengths) { - const pxNum = Number( - (absoluteLengths[units] * Number(match[1])).toFixed( - floatPrecision, - ), - ); - if (pxNum.toString().length < match[0].length) { - num = pxNum; - units = 'px'; - } - } - - // and remove leading zero - let str; - if (leadingZero) { - str = removeLeadingZero(num); - } else { - str = num.toString(); - } - - // remove default 'px' units - if (defaultPx && units === 'px') { - units = ''; - } - - node.attributes[name] = str + units; - } - } - }, - }, - }; -}; diff --git a/node_modules/svgo/plugins/collapseGroups.js b/node_modules/svgo/plugins/collapseGroups.js deleted file mode 100644 index 0534ad77..00000000 --- a/node_modules/svgo/plugins/collapseGroups.js +++ /dev/null @@ -1,134 +0,0 @@ -'use strict'; - -/** - * @typedef {import('../lib/types').XastNode} XastNode - */ - -const { inheritableAttrs, elemsGroups } = require('./_collections.js'); - -exports.name = 'collapseGroups'; -exports.description = 'collapses useless groups'; - -/** - * @type {(node: XastNode, name: string) => boolean} - */ -const hasAnimatedAttr = (node, name) => { - if (node.type === 'element') { - if ( - elemsGroups.animation.has(node.name) && - node.attributes.attributeName === name - ) { - return true; - } - for (const child of node.children) { - if (hasAnimatedAttr(child, name)) { - return true; - } - } - } - return false; -}; - -/** - * Collapse useless groups. - * - * @example - * <g> - * <g attr1="val1"> - * <path d="..."/> - * </g> - * </g> - * ⬇ - * <g> - * <g> - * <path attr1="val1" d="..."/> - * </g> - * </g> - * ⬇ - * <path attr1="val1" d="..."/> - * - * @author Kir Belevich - * - * @type {import('./plugins-types').Plugin<'collapseGroups'>} - */ -exports.fn = () => { - return { - element: { - exit: (node, parentNode) => { - if (parentNode.type === 'root' || parentNode.name === 'switch') { - return; - } - // non-empty groups - if (node.name !== 'g' || node.children.length === 0) { - return; - } - - // move group attributes to the single child element - if ( - Object.keys(node.attributes).length !== 0 && - node.children.length === 1 - ) { - const firstChild = node.children[0]; - // TODO untangle this mess - if ( - firstChild.type === 'element' && - firstChild.attributes.id == null && - node.attributes.filter == null && - (node.attributes.class == null || - firstChild.attributes.class == null) && - ((node.attributes['clip-path'] == null && - node.attributes.mask == null) || - (firstChild.name === 'g' && - node.attributes.transform == null && - firstChild.attributes.transform == null)) - ) { - for (const [name, value] of Object.entries(node.attributes)) { - // avoid copying to not conflict with animated attribute - if (hasAnimatedAttr(firstChild, name)) { - return; - } - if (firstChild.attributes[name] == null) { - firstChild.attributes[name] = value; - } else if (name === 'transform') { - firstChild.attributes[name] = - value + ' ' + firstChild.attributes[name]; - } else if (firstChild.attributes[name] === 'inherit') { - firstChild.attributes[name] = value; - } else if ( - inheritableAttrs.has(name) === false && - firstChild.attributes[name] !== value - ) { - return; - } - delete node.attributes[name]; - } - } - } - - // collapse groups without attributes - if (Object.keys(node.attributes).length === 0) { - // animation elements "add" attributes to group - // group should be preserved - for (const child of node.children) { - if ( - child.type === 'element' && - elemsGroups.animation.has(child.name) - ) { - return; - } - } - // replace current node with all its children - const index = parentNode.children.indexOf(node); - parentNode.children.splice(index, 1, ...node.children); - // TODO remove legacy parentNode in v4 - for (const child of node.children) { - Object.defineProperty(child, 'parentNode', { - writable: true, - value: parentNode, - }); - } - } - }, - }, - }; -}; diff --git a/node_modules/svgo/plugins/convertColors.js b/node_modules/svgo/plugins/convertColors.js deleted file mode 100644 index d894283f..00000000 --- a/node_modules/svgo/plugins/convertColors.js +++ /dev/null @@ -1,144 +0,0 @@ -'use strict'; - -const collections = require('./_collections.js'); - -exports.name = 'convertColors'; -exports.description = 'converts colors: rgb() to #rrggbb and #rrggbb to #rgb'; - -const rNumber = '([+-]?(?:\\d*\\.\\d+|\\d+\\.?)%?)'; -const rComma = '\\s*,\\s*'; -const regRGB = new RegExp( - '^rgb\\(\\s*' + rNumber + rComma + rNumber + rComma + rNumber + '\\s*\\)$', -); -const regHEX = /^#(([a-fA-F0-9])\2){3}$/; - -/** - * Convert [r, g, b] to #rrggbb. - * - * @see https://gist.github.com/983535 - * - * @example - * rgb2hex([255, 255, 255]) // '#ffffff' - * - * @author Jed Schmidt - * - * @type {(rgb: number[]) => string} - */ -const convertRgbToHex = ([r, g, b]) => { - // combine the octets into a 32-bit integer as: [1][r][g][b] - const hexNumber = - // operator precedence is (+) > (<<) > (|) - ((((256 + // [1][0] - r) << // [1][r] - 8) | // [1][r][0] - g) << // [1][r][g] - 8) | // [1][r][g][0] - b; - // serialize [1][r][g][b] to a hex string, and - // remove the 1 to get the number with 0s intact - return '#' + hexNumber.toString(16).slice(1).toUpperCase(); -}; - -/** - * Convert different colors formats in element attributes to hex. - * - * @see https://www.w3.org/TR/SVG11/types.html#DataTypeColor - * @see https://www.w3.org/TR/SVG11/single-page.html#types-ColorKeywords - * - * @example - * Convert color name keyword to long hex: - * fuchsia ➡ #ff00ff - * - * Convert rgb() to long hex: - * rgb(255, 0, 255) ➡ #ff00ff - * rgb(50%, 100, 100%) ➡ #7f64ff - * - * Convert long hex to short hex: - * #aabbcc ➡ #abc - * - * Convert hex to short name - * #000080 ➡ navy - * - * @author Kir Belevich - * - * @type {import('./plugins-types').Plugin<'convertColors'>} - */ -exports.fn = (_root, params) => { - const { - currentColor = false, - names2hex = true, - rgb2hex = true, - shorthex = true, - shortname = true, - } = params; - - return { - element: { - enter: (node) => { - for (const [name, value] of Object.entries(node.attributes)) { - if (collections.colorsProps.has(name)) { - let val = value; - - // convert colors to currentColor - if (currentColor) { - let matched; - if (typeof currentColor === 'string') { - matched = val === currentColor; - } else if (currentColor instanceof RegExp) { - matched = currentColor.exec(val) != null; - } else { - matched = val !== 'none'; - } - if (matched) { - val = 'currentColor'; - } - } - - // convert color name keyword to long hex - if (names2hex) { - const colorName = val.toLowerCase(); - if (collections.colorsNames[colorName] != null) { - val = collections.colorsNames[colorName]; - } - } - - // convert rgb() to long hex - if (rgb2hex) { - let match = val.match(regRGB); - if (match != null) { - let nums = match.slice(1, 4).map((m) => { - let n; - if (m.indexOf('%') > -1) { - n = Math.round(parseFloat(m) * 2.55); - } else { - n = Number(m); - } - return Math.max(0, Math.min(n, 255)); - }); - val = convertRgbToHex(nums); - } - } - - // convert long hex to short hex - if (shorthex) { - let match = val.match(regHEX); - if (match != null) { - val = '#' + match[0][1] + match[0][3] + match[0][5]; - } - } - - // convert hex to short name - if (shortname) { - const colorName = val.toLowerCase(); - if (collections.colorsShortNames[colorName] != null) { - val = collections.colorsShortNames[colorName]; - } - } - - node.attributes[name] = val; - } - } - }, - }, - }; -}; diff --git a/node_modules/svgo/plugins/convertEllipseToCircle.js b/node_modules/svgo/plugins/convertEllipseToCircle.js deleted file mode 100644 index 50fa20db..00000000 --- a/node_modules/svgo/plugins/convertEllipseToCircle.js +++ /dev/null @@ -1,37 +0,0 @@ -'use strict'; - -exports.name = 'convertEllipseToCircle'; -exports.description = 'converts non-eccentric <ellipse>s to <circle>s'; - -/** - * Converts non-eccentric <ellipse>s to <circle>s. - * - * @see https://www.w3.org/TR/SVG11/shapes.html - * - * @author Taylor Hunt - * - * @type {import('./plugins-types').Plugin<'convertEllipseToCircle'>} - */ -exports.fn = () => { - return { - element: { - enter: (node) => { - if (node.name === 'ellipse') { - const rx = node.attributes.rx || '0'; - const ry = node.attributes.ry || '0'; - if ( - rx === ry || - rx === 'auto' || - ry === 'auto' // SVG2 - ) { - node.name = 'circle'; - const radius = rx === 'auto' ? ry : rx; - delete node.attributes.rx; - delete node.attributes.ry; - node.attributes.r = radius; - } - } - }, - }, - }; -}; diff --git a/node_modules/svgo/plugins/convertOneStopGradients.js b/node_modules/svgo/plugins/convertOneStopGradients.js deleted file mode 100644 index 1cf0d66b..00000000 --- a/node_modules/svgo/plugins/convertOneStopGradients.js +++ /dev/null @@ -1,168 +0,0 @@ -'use strict'; - -/** - * @typedef {import('../lib/types').XastElement} XastElement - * @typedef {import('../lib/types').XastParent} XastParent - */ - -const { attrsGroupsDefaults, colorsProps } = require('./_collections'); -const { - detachNodeFromParent, - querySelectorAll, - querySelector, -} = require('../lib/xast'); -const { computeStyle, collectStylesheet } = require('../lib/style'); - -exports.name = 'convertOneStopGradients'; -exports.description = - 'converts one-stop (single color) gradients to a plain color'; - -/** - * Converts one-stop (single color) gradients to a plain color. - * - * @author Seth Falco <seth@falco.fun> - * @type {import('./plugins-types').Plugin<'convertOneStopGradients'>} - * @see https://developer.mozilla.org/docs/Web/SVG/Element/linearGradient - * @see https://developer.mozilla.org/docs/Web/SVG/Element/radialGradient - */ -exports.fn = (root) => { - const stylesheet = collectStylesheet(root); - - /** - * Parent defs that had gradients elements removed from them. - * - * @type {Set<XastElement>} - */ - const effectedDefs = new Set(); - - /** - * @type {Map<XastElement, XastParent>} - */ - const allDefs = new Map(); - - /** - * @type {Map<XastElement, XastParent>} - */ - const gradientsToDetach = new Map(); - - /** Number of references to the xlink:href attribute. */ - let xlinkHrefCount = 0; - - return { - element: { - enter: (node, parentNode) => { - if (node.attributes['xlink:href'] != null) { - xlinkHrefCount++; - } - - if (node.name === 'defs') { - allDefs.set(node, parentNode); - return; - } - - if (node.name !== 'linearGradient' && node.name !== 'radialGradient') { - return; - } - - const stops = node.children.filter((child) => { - return child.type === 'element' && child.name === 'stop'; - }); - - const href = node.attributes['xlink:href'] || node.attributes['href']; - let effectiveNode = - stops.length === 0 && href != null && href.startsWith('#') - ? querySelector(root, href) - : node; - - if (effectiveNode == null || effectiveNode.type !== 'element') { - gradientsToDetach.set(node, parentNode); - return; - } - - const effectiveStops = effectiveNode.children.filter((child) => { - return child.type === 'element' && child.name === 'stop'; - }); - - if ( - effectiveStops.length !== 1 || - effectiveStops[0].type !== 'element' - ) { - return; - } - - if (parentNode.type === 'element' && parentNode.name === 'defs') { - effectedDefs.add(parentNode); - } - - gradientsToDetach.set(node, parentNode); - - let color; - const style = computeStyle(stylesheet, effectiveStops[0])['stop-color']; - if (style != null && style.type === 'static') { - color = style.value; - } - - const selectorVal = `url(#${node.attributes.id})`; - - const selector = [...colorsProps] - .map((attr) => `[${attr}="${selectorVal}"]`) - .join(','); - const elements = querySelectorAll(root, selector); - for (const element of elements) { - if (element.type !== 'element') { - continue; - } - - for (const attr of colorsProps) { - if (element.attributes[attr] !== selectorVal) { - continue; - } - - if (color != null) { - element.attributes[attr] = color; - } else { - delete element.attributes[attr]; - } - } - } - - const styledElements = querySelectorAll( - root, - `[style*=${selectorVal}]`, - ); - for (const element of styledElements) { - if (element.type !== 'element') { - continue; - } - - element.attributes.style = element.attributes.style.replace( - selectorVal, - color || attrsGroupsDefaults.presentation['stop-color'], - ); - } - }, - - exit: (node) => { - if (node.name === 'svg') { - for (const [gradient, parent] of gradientsToDetach.entries()) { - if (gradient.attributes['xlink:href'] != null) { - xlinkHrefCount--; - } - - detachNodeFromParent(gradient, parent); - } - - if (xlinkHrefCount === 0) { - delete node.attributes['xmlns:xlink']; - } - - for (const [defs, parent] of allDefs.entries()) { - if (effectedDefs.has(defs) && defs.children.length === 0) { - detachNodeFromParent(defs, parent); - } - } - } - }, - }, - }; -}; diff --git a/node_modules/svgo/plugins/convertPathData.js b/node_modules/svgo/plugins/convertPathData.js deleted file mode 100644 index de7b357f..00000000 --- a/node_modules/svgo/plugins/convertPathData.js +++ /dev/null @@ -1,1284 +0,0 @@ -'use strict'; - -/** - * @typedef {import('../lib//types').PathDataItem} PathDataItem - */ - -const { collectStylesheet, computeStyle } = require('../lib/style.js'); -const { visit } = require('../lib/xast.js'); -const { pathElems } = require('./_collections.js'); -const { path2js, js2path } = require('./_path.js'); -const { applyTransforms } = require('./applyTransforms.js'); -const { cleanupOutData, toFixed } = require('../lib/svgo/tools'); - -exports.name = 'convertPathData'; -exports.description = - 'optimizes path data: writes in shorter form, applies transformations'; - -/** @type {(data: number[]) => number[]} */ -let roundData; -/** @type {number | false} */ -let precision; -/** @type {number} */ -let error; -/** @type {number} */ -let arcThreshold; -/** @type {number} */ -let arcTolerance; - -/** - * @typedef {{ - * applyTransforms: boolean, - * applyTransformsStroked: boolean, - * makeArcs: { - * threshold: number, - * tolerance: number, - * }, - * straightCurves: boolean, - * convertToQ: boolean, - * lineShorthands: boolean, - * convertToZ: boolean, - * curveSmoothShorthands: boolean, - * floatPrecision: number | false, - * transformPrecision: number, - * smartArcRounding: boolean, - * removeUseless: boolean, - * collapseRepeated: boolean, - * utilizeAbsolute: boolean, - * leadingZero: boolean, - * negativeExtraSpace: boolean, - * noSpaceAfterFlags: boolean, - * forceAbsolutePath: boolean, - * }} InternalParams - */ - -/** - * @typedef {[number, number]} Point - */ - -/** - * @typedef {{ - * center: Point, - * radius: number - * }} Circle - */ - -/** - * Convert absolute Path to relative, - * collapse repeated instructions, - * detect and convert Lineto shorthands, - * remove useless instructions like "l0,0", - * trim useless delimiters and leading zeros, - * decrease accuracy of floating-point numbers. - * - * @see https://www.w3.org/TR/SVG11/paths.html#PathData - * - * @author Kir Belevich - * - * @type {import('./plugins-types').Plugin<'convertPathData'>} - */ -exports.fn = (root, params) => { - const { - // TODO convert to separate plugin in v3 - applyTransforms: _applyTransforms = true, - applyTransformsStroked = true, - makeArcs = { - threshold: 2.5, // coefficient of rounding error - tolerance: 0.5, // percentage of radius - }, - straightCurves = true, - convertToQ = true, - lineShorthands = true, - convertToZ = true, - curveSmoothShorthands = true, - floatPrecision = 3, - transformPrecision = 5, - smartArcRounding = true, - removeUseless = true, - collapseRepeated = true, - utilizeAbsolute = true, - leadingZero = true, - negativeExtraSpace = true, - noSpaceAfterFlags = false, // a20 60 45 0 1 30 20 → a20 60 45 0130 20 - forceAbsolutePath = false, - } = params; - - /** - * @type {InternalParams} - */ - const newParams = { - applyTransforms: _applyTransforms, - applyTransformsStroked, - makeArcs, - straightCurves, - convertToQ, - lineShorthands, - convertToZ, - curveSmoothShorthands, - floatPrecision, - transformPrecision, - smartArcRounding, - removeUseless, - collapseRepeated, - utilizeAbsolute, - leadingZero, - negativeExtraSpace, - noSpaceAfterFlags, - forceAbsolutePath, - }; - - // invoke applyTransforms plugin - if (_applyTransforms) { - visit( - root, - // @ts-ignore - applyTransforms(root, { - transformPrecision, - applyTransformsStroked, - }), - ); - } - - const stylesheet = collectStylesheet(root); - return { - element: { - enter: (node) => { - if (pathElems.has(node.name) && node.attributes.d != null) { - const computedStyle = computeStyle(stylesheet, node); - precision = floatPrecision; - error = - precision !== false - ? +Math.pow(0.1, precision).toFixed(precision) - : 1e-2; - roundData = - precision && precision > 0 && precision < 20 ? strongRound : round; - if (makeArcs) { - arcThreshold = makeArcs.threshold; - arcTolerance = makeArcs.tolerance; - } - const hasMarkerMid = computedStyle['marker-mid'] != null; - - const maybeHasStroke = - computedStyle.stroke && - (computedStyle.stroke.type === 'dynamic' || - computedStyle.stroke.value !== 'none'); - const maybeHasLinecap = - computedStyle['stroke-linecap'] && - (computedStyle['stroke-linecap'].type === 'dynamic' || - computedStyle['stroke-linecap'].value !== 'butt'); - const maybeHasStrokeAndLinecap = maybeHasStroke && maybeHasLinecap; - const isSafeToUseZ = maybeHasStroke - ? computedStyle['stroke-linecap']?.type === 'static' && - computedStyle['stroke-linecap'].value === 'round' && - computedStyle['stroke-linejoin']?.type === 'static' && - computedStyle['stroke-linejoin'].value === 'round' - : true; - - var data = path2js(node); - - // TODO: get rid of functions returns - if (data.length) { - convertToRelative(data); - - data = filters(data, newParams, { - isSafeToUseZ, - maybeHasStrokeAndLinecap, - hasMarkerMid, - }); - - if (utilizeAbsolute) { - data = convertToMixed(data, newParams); - } - - // @ts-ignore - js2path(node, data, newParams); - } - } - }, - }, - }; -}; - -/** - * Convert absolute path data coordinates to relative. - * - * @type {(pathData: PathDataItem[]) => PathDataItem[]} - */ -const convertToRelative = (pathData) => { - let start = [0, 0]; - let cursor = [0, 0]; - let prevCoords = [0, 0]; - - for (let i = 0; i < pathData.length; i += 1) { - const pathItem = pathData[i]; - let { command, args } = pathItem; - - // moveto (x y) - if (command === 'm') { - // update start and cursor - cursor[0] += args[0]; - cursor[1] += args[1]; - start[0] = cursor[0]; - start[1] = cursor[1]; - } - if (command === 'M') { - // M → m - // skip first moveto - if (i !== 0) { - command = 'm'; - } - args[0] -= cursor[0]; - args[1] -= cursor[1]; - // update start and cursor - cursor[0] += args[0]; - cursor[1] += args[1]; - start[0] = cursor[0]; - start[1] = cursor[1]; - } - - // lineto (x y) - if (command === 'l') { - cursor[0] += args[0]; - cursor[1] += args[1]; - } - if (command === 'L') { - // L → l - command = 'l'; - args[0] -= cursor[0]; - args[1] -= cursor[1]; - cursor[0] += args[0]; - cursor[1] += args[1]; - } - - // horizontal lineto (x) - if (command === 'h') { - cursor[0] += args[0]; - } - if (command === 'H') { - // H → h - command = 'h'; - args[0] -= cursor[0]; - cursor[0] += args[0]; - } - - // vertical lineto (y) - if (command === 'v') { - cursor[1] += args[0]; - } - if (command === 'V') { - // V → v - command = 'v'; - args[0] -= cursor[1]; - cursor[1] += args[0]; - } - - // curveto (x1 y1 x2 y2 x y) - if (command === 'c') { - cursor[0] += args[4]; - cursor[1] += args[5]; - } - if (command === 'C') { - // C → c - command = 'c'; - args[0] -= cursor[0]; - args[1] -= cursor[1]; - args[2] -= cursor[0]; - args[3] -= cursor[1]; - args[4] -= cursor[0]; - args[5] -= cursor[1]; - cursor[0] += args[4]; - cursor[1] += args[5]; - } - - // smooth curveto (x2 y2 x y) - if (command === 's') { - cursor[0] += args[2]; - cursor[1] += args[3]; - } - if (command === 'S') { - // S → s - command = 's'; - args[0] -= cursor[0]; - args[1] -= cursor[1]; - args[2] -= cursor[0]; - args[3] -= cursor[1]; - cursor[0] += args[2]; - cursor[1] += args[3]; - } - - // quadratic Bézier curveto (x1 y1 x y) - if (command === 'q') { - cursor[0] += args[2]; - cursor[1] += args[3]; - } - if (command === 'Q') { - // Q → q - command = 'q'; - args[0] -= cursor[0]; - args[1] -= cursor[1]; - args[2] -= cursor[0]; - args[3] -= cursor[1]; - cursor[0] += args[2]; - cursor[1] += args[3]; - } - - // smooth quadratic Bézier curveto (x y) - if (command === 't') { - cursor[0] += args[0]; - cursor[1] += args[1]; - } - if (command === 'T') { - // T → t - command = 't'; - args[0] -= cursor[0]; - args[1] -= cursor[1]; - cursor[0] += args[0]; - cursor[1] += args[1]; - } - - // elliptical arc (rx ry x-axis-rotation large-arc-flag sweep-flag x y) - if (command === 'a') { - cursor[0] += args[5]; - cursor[1] += args[6]; - } - if (command === 'A') { - // A → a - command = 'a'; - args[5] -= cursor[0]; - args[6] -= cursor[1]; - cursor[0] += args[5]; - cursor[1] += args[6]; - } - - // closepath - if (command === 'Z' || command === 'z') { - // reset cursor - cursor[0] = start[0]; - cursor[1] = start[1]; - } - - pathItem.command = command; - pathItem.args = args; - // store absolute coordinates for later use - // base should preserve reference from other element - // @ts-ignore - pathItem.base = prevCoords; - // @ts-ignore - pathItem.coords = [cursor[0], cursor[1]]; - // @ts-ignore - prevCoords = pathItem.coords; - } - - return pathData; -}; - -/** - * Main filters loop. - * - * @type {( - * path: PathDataItem[], - * params: InternalParams, - * aux: { isSafeToUseZ: boolean, maybeHasStrokeAndLinecap: boolean, hasMarkerMid: boolean } - * ) => PathDataItem[]} - */ -function filters( - path, - params, - { isSafeToUseZ, maybeHasStrokeAndLinecap, hasMarkerMid }, -) { - const stringify = data2Path.bind(null, params); - const relSubpoint = [0, 0]; - const pathBase = [0, 0]; - /** @type {any} */ - let prev = {}; - /** @type {Point | undefined} */ - let prevQControlPoint; - - path = path.filter(function (item, index, path) { - const qControlPoint = prevQControlPoint; - prevQControlPoint = undefined; - - let command = item.command; - let data = item.args; - let next = path[index + 1]; - - if (command !== 'Z' && command !== 'z') { - var sdata = data, - circle; - - if (command === 's') { - sdata = [0, 0].concat(data); - - const pdata = prev.args; - const n = pdata.length; - - // (-x, -y) of the prev tangent point relative to the current point - sdata[0] = pdata[n - 2] - pdata[n - 4]; - sdata[1] = pdata[n - 1] - pdata[n - 3]; - } - - // convert curves to arcs if possible - if ( - params.makeArcs && - (command == 'c' || command == 's') && - isConvex(sdata) && - (circle = findCircle(sdata)) - ) { - var r = roundData([circle.radius])[0], - angle = findArcAngle(sdata, circle), - sweep = sdata[5] * sdata[0] - sdata[4] * sdata[1] > 0 ? 1 : 0, - /** - * @type {PathDataItem} - */ - arc = { - command: 'a', - args: [r, r, 0, 0, sweep, sdata[4], sdata[5]], - // @ts-ignore - coords: item.coords.slice(), - // @ts-ignore - base: item.base, - }, - /** - * @type {PathDataItem[]} - */ - output = [arc], - // relative coordinates to adjust the found circle - /** - * @type {Point} - */ - relCenter = [ - circle.center[0] - sdata[4], - circle.center[1] - sdata[5], - ], - relCircle = { center: relCenter, radius: circle.radius }, - arcCurves = [item], - hasPrev = 0, - suffix = '', - nextLonghand; - - if ( - (prev.command == 'c' && - isConvex(prev.args) && - isArcPrev(prev.args, circle)) || - (prev.command == 'a' && prev.sdata && isArcPrev(prev.sdata, circle)) - ) { - arcCurves.unshift(prev); - // @ts-ignore - arc.base = prev.base; - // @ts-ignore - arc.args[5] = arc.coords[0] - arc.base[0]; - // @ts-ignore - arc.args[6] = arc.coords[1] - arc.base[1]; - var prevData = prev.command == 'a' ? prev.sdata : prev.args; - var prevAngle = findArcAngle(prevData, { - center: [ - prevData[4] + circle.center[0], - prevData[5] + circle.center[1], - ], - radius: circle.radius, - }); - angle += prevAngle; - if (angle > Math.PI) arc.args[3] = 1; - hasPrev = 1; - } - - // check if next curves are fitting the arc - for ( - var j = index; - (next = path[++j]) && (next.command === 'c' || next.command === 's'); - - ) { - var nextData = next.args; - if (next.command == 's') { - nextLonghand = makeLonghand( - { command: 's', args: next.args.slice() }, - path[j - 1].args, - ); - nextData = nextLonghand.args; - nextLonghand.args = nextData.slice(0, 2); - suffix = stringify([nextLonghand]); - } - if (isConvex(nextData) && isArc(nextData, relCircle)) { - angle += findArcAngle(nextData, relCircle); - if (angle - 2 * Math.PI > 1e-3) break; // more than 360° - if (angle > Math.PI) arc.args[3] = 1; - arcCurves.push(next); - if (2 * Math.PI - angle > 1e-3) { - // less than 360° - // @ts-ignore - arc.coords = next.coords; - // @ts-ignore - arc.args[5] = arc.coords[0] - arc.base[0]; - // @ts-ignore - arc.args[6] = arc.coords[1] - arc.base[1]; - } else { - // full circle, make a half-circle arc and add a second one - arc.args[5] = 2 * (relCircle.center[0] - nextData[4]); - arc.args[6] = 2 * (relCircle.center[1] - nextData[5]); - // @ts-ignore - arc.coords = [ - // @ts-ignore - arc.base[0] + arc.args[5], - // @ts-ignore - arc.base[1] + arc.args[6], - ]; - arc = { - command: 'a', - args: [ - r, - r, - 0, - 0, - sweep, - // @ts-ignore - next.coords[0] - arc.coords[0], - // @ts-ignore - next.coords[1] - arc.coords[1], - ], - // @ts-ignore - coords: next.coords, - // @ts-ignore - base: arc.coords, - }; - output.push(arc); - j++; - break; - } - relCenter[0] -= nextData[4]; - relCenter[1] -= nextData[5]; - } else break; - } - - if ((stringify(output) + suffix).length < stringify(arcCurves).length) { - if (path[j] && path[j].command == 's') { - makeLonghand(path[j], path[j - 1].args); - } - if (hasPrev) { - var prevArc = output.shift(); - // @ts-ignore - roundData(prevArc.args); - // @ts-ignore - relSubpoint[0] += prevArc.args[5] - prev.args[prev.args.length - 2]; - // @ts-ignore - relSubpoint[1] += prevArc.args[6] - prev.args[prev.args.length - 1]; - prev.command = 'a'; - // @ts-ignore - prev.args = prevArc.args; - // @ts-ignore - item.base = prev.coords = prevArc.coords; - } - // @ts-ignore - arc = output.shift(); - if (arcCurves.length == 1) { - // @ts-ignore - item.sdata = sdata.slice(); // preserve curve data for future checks - } else if (arcCurves.length - 1 - hasPrev > 0) { - // filter out consumed next items - path.splice(index + 1, arcCurves.length - 1 - hasPrev, ...output); - } - if (!arc) return false; - command = 'a'; - data = arc.args; - // @ts-ignore - item.coords = arc.coords; - } - } - - // Rounding relative coordinates, taking in account accumulating error - // to get closer to absolute coordinates. Sum of rounded value remains same: - // l .25 3 .25 2 .25 3 .25 2 -> l .3 3 .2 2 .3 3 .2 2 - if (precision !== false) { - if ( - command === 'm' || - command === 'l' || - command === 't' || - command === 'q' || - command === 's' || - command === 'c' - ) { - for (var i = data.length; i--; ) { - // @ts-ignore - data[i] += item.base[i % 2] - relSubpoint[i % 2]; - } - } else if (command == 'h') { - // @ts-ignore - data[0] += item.base[0] - relSubpoint[0]; - } else if (command == 'v') { - // @ts-ignore - data[0] += item.base[1] - relSubpoint[1]; - } else if (command == 'a') { - // @ts-ignore - data[5] += item.base[0] - relSubpoint[0]; - // @ts-ignore - data[6] += item.base[1] - relSubpoint[1]; - } - roundData(data); - - if (command == 'h') relSubpoint[0] += data[0]; - else if (command == 'v') relSubpoint[1] += data[0]; - else { - relSubpoint[0] += data[data.length - 2]; - relSubpoint[1] += data[data.length - 1]; - } - roundData(relSubpoint); - - if (command === 'M' || command === 'm') { - pathBase[0] = relSubpoint[0]; - pathBase[1] = relSubpoint[1]; - } - } - - // round arc radius more accurately - // eg m 0 0 a 1234.567 1234.567 0 0 1 10 0 -> m 0 0 a 1235 1235 0 0 1 10 0 - const sagitta = command === 'a' ? calculateSagitta(data) : undefined; - if (params.smartArcRounding && sagitta !== undefined && precision) { - for (let precisionNew = precision; precisionNew >= 0; precisionNew--) { - const radius = toFixed(data[0], precisionNew); - const sagittaNew = /** @type {number} */ ( - calculateSagitta([radius, radius, ...data.slice(2)]) - ); - if (Math.abs(sagitta - sagittaNew) < error) { - data[0] = radius; - data[1] = radius; - } else { - break; - } - } - } - - // convert straight curves into lines segments - if (params.straightCurves) { - if ( - (command === 'c' && isCurveStraightLine(data)) || - (command === 's' && isCurveStraightLine(sdata)) - ) { - if (next && next.command == 's') makeLonghand(next, data); // fix up next curve - command = 'l'; - data = data.slice(-2); - } else if (command === 'q' && isCurveStraightLine(data)) { - if (next && next.command == 't') makeLonghand(next, data); // fix up next curve - 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' && - (data[0] === 0 || - data[1] === 0 || - (sagitta !== undefined && sagitta < error)) - ) { - command = 'l'; - data = data.slice(-2); - } - } - - // degree-lower c to q when possible - // m 0 12 C 4 4 8 4 12 12 → M 0 12 Q 6 0 12 12 - if (params.convertToQ && command == 'c') { - const x1 = - // @ts-ignore - 0.75 * (item.base[0] + data[0]) - 0.25 * item.base[0]; - const x2 = - // @ts-ignore - 0.75 * (item.base[0] + data[2]) - 0.25 * (item.base[0] + data[4]); - if (Math.abs(x1 - x2) < error * 2) { - const y1 = - // @ts-ignore - 0.75 * (item.base[1] + data[1]) - 0.25 * item.base[1]; - const y2 = - // @ts-ignore - 0.75 * (item.base[1] + data[3]) - 0.25 * (item.base[1] + data[5]); - if (Math.abs(y1 - y2) < error * 2) { - const newData = data.slice(); - newData.splice( - 0, - 4, - // @ts-ignore - x1 + x2 - item.base[0], - // @ts-ignore - y1 + y2 - item.base[1], - ); - roundData(newData); - const originalLength = cleanupOutData(data, params).length, - newLength = cleanupOutData(newData, params).length; - if (newLength < originalLength) { - command = 'q'; - data = newData; - if (next && next.command == 's') makeLonghand(next, data); // fix up next curve - } - } - } - } - - // horizontal and vertical line shorthands - // l 50 0 → h 50 - // l 0 50 → v 50 - if (params.lineShorthands && command === 'l') { - if (data[1] === 0) { - command = 'h'; - data.pop(); - } else if (data[0] === 0) { - command = 'v'; - data.shift(); - } - } - - // collapse repeated commands - // h 20 h 30 -> h 50 - if ( - params.collapseRepeated && - hasMarkerMid === false && - (command === 'm' || command === 'h' || command === 'v') && - prev.command && - command == prev.command.toLowerCase() && - ((command != 'h' && command != 'v') || - prev.args[0] >= 0 == data[0] >= 0) - ) { - prev.args[0] += data[0]; - if (command != 'h' && command != 'v') { - prev.args[1] += data[1]; - } - // @ts-ignore - prev.coords = item.coords; - path[index] = prev; - return false; - } - - // convert curves into smooth shorthands - if (params.curveSmoothShorthands && prev.command) { - // curveto - if (command === 'c') { - // c + c → c + s - if ( - prev.command === 'c' && - Math.abs(data[0] - -(prev.args[2] - prev.args[4])) < error && - Math.abs(data[1] - -(prev.args[3] - prev.args[5])) < error - ) { - command = 's'; - data = data.slice(2); - } - - // s + c → s + s - else if ( - prev.command === 's' && - Math.abs(data[0] - -(prev.args[0] - prev.args[2])) < error && - Math.abs(data[1] - -(prev.args[1] - prev.args[3])) < error - ) { - command = 's'; - data = data.slice(2); - } - - // [^cs] + c → [^cs] + s - else if ( - prev.command !== 'c' && - prev.command !== 's' && - Math.abs(data[0]) < error && - Math.abs(data[1]) < error - ) { - command = 's'; - data = data.slice(2); - } - } - - // quadratic Bézier curveto - else if (command === 'q') { - // q + q → q + t - if ( - prev.command === 'q' && - Math.abs(data[0] - (prev.args[2] - prev.args[0])) < error && - Math.abs(data[1] - (prev.args[3] - prev.args[1])) < error - ) { - command = 't'; - data = data.slice(2); - } - - // t + q → t + t - else if (prev.command === 't') { - const predictedControlPoint = reflectPoint( - // @ts-ignore - qControlPoint, - // @ts-ignore - item.base, - ); - const realControlPoint = [ - // @ts-ignore - data[0] + item.base[0], - // @ts-ignore - data[1] + item.base[1], - ]; - if ( - Math.abs(predictedControlPoint[0] - realControlPoint[0]) < - error && - Math.abs(predictedControlPoint[1] - realControlPoint[1]) < error - ) { - command = 't'; - data = data.slice(2); - } - } - } - } - - // remove useless non-first path segments - if (params.removeUseless && !maybeHasStrokeAndLinecap) { - // 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 - if ( - (command === 'l' || - command === 'h' || - command === 'v' || - command === 'q' || - command === 't' || - command === 'c' || - command === 's') && - data.every(function (i) { - return i === 0; - }) - ) { - path[index] = prev; - return false; - } - - // a 25,25 -30 0,1 0,0 - if (command === 'a' && data[5] === 0 && data[6] === 0) { - path[index] = prev; - return false; - } - } - - // convert going home to z - // m 0 0 h 5 v 5 l -5 -5 -> m 0 0 h 5 v 5 z - if ( - params.convertToZ && - (isSafeToUseZ || next?.command === 'Z' || next?.command === 'z') && - (command === 'l' || command === 'h' || command === 'v') - ) { - if ( - // @ts-ignore - Math.abs(pathBase[0] - item.coords[0]) < error && - // @ts-ignore - Math.abs(pathBase[1] - item.coords[1]) < error - ) { - command = 'z'; - data = []; - } - } - - item.command = command; - item.args = data; - } else { - // z resets coordinates - relSubpoint[0] = pathBase[0]; - relSubpoint[1] = pathBase[1]; - if (prev.command === 'Z' || prev.command === 'z') return false; - } - if ( - (command === 'Z' || command === 'z') && - params.removeUseless && - isSafeToUseZ && - // @ts-ignore - Math.abs(item.base[0] - item.coords[0]) < error / 10 && - // @ts-ignore - Math.abs(item.base[1] - item.coords[1]) < error / 10 - ) - return false; - - if (command === 'q') { - // @ts-ignore - prevQControlPoint = [data[0] + item.base[0], data[1] + item.base[1]]; - } else if (command === 't') { - if (qControlPoint) { - // @ts-ignore - prevQControlPoint = reflectPoint(qControlPoint, item.base); - } else { - // @ts-ignore - prevQControlPoint = item.coords; - } - } - prev = item; - return true; - }); - - return path; -} - -/** - * Writes data in shortest form using absolute or relative coordinates. - * - * @type {(path: PathDataItem[], params: InternalParams) => PathDataItem[]} - */ -function convertToMixed(path, params) { - var prev = path[0]; - - path = path.filter(function (item, index) { - if (index == 0) return true; - if (item.command === 'Z' || item.command === 'z') { - prev = item; - return true; - } - - var command = item.command, - data = item.args, - adata = data.slice(), - rdata = data.slice(); - - if ( - command === 'm' || - command === 'l' || - command === 't' || - command === 'q' || - command === 's' || - command === 'c' - ) { - for (var i = adata.length; i--; ) { - // @ts-ignore - adata[i] += item.base[i % 2]; - } - } else if (command == 'h') { - // @ts-ignore - adata[0] += item.base[0]; - } else if (command == 'v') { - // @ts-ignore - adata[0] += item.base[1]; - } else if (command == 'a') { - // @ts-ignore - adata[5] += item.base[0]; - // @ts-ignore - adata[6] += item.base[1]; - } - - roundData(adata); - roundData(rdata); - - var absoluteDataStr = cleanupOutData(adata, params), - relativeDataStr = cleanupOutData(rdata, params); - - // Convert to absolute coordinates if it's shorter or forceAbsolutePath is true. - // v-20 -> V0 - // Don't convert if it fits following previous command. - // l20 30-10-50 instead of l20 30L20 30 - if ( - params.forceAbsolutePath || - (absoluteDataStr.length < relativeDataStr.length && - !( - params.negativeExtraSpace && - command == prev.command && - prev.command.charCodeAt(0) > 96 && - absoluteDataStr.length == relativeDataStr.length - 1 && - (data[0] < 0 || - (Math.floor(data[0]) === 0 && - !Number.isInteger(data[0]) && - prev.args[prev.args.length - 1] % 1)) - )) - ) { - // @ts-ignore - item.command = command.toUpperCase(); - item.args = adata; - } - - prev = item; - return true; - }); - - return path; -} - -/** - * Checks if curve is convex. Control points of such a curve must form - * a convex quadrilateral with diagonals crosspoint inside of it. - * - * @type {(data: number[]) => boolean} - */ -function isConvex(data) { - var center = getIntersection([ - 0, - 0, - data[2], - data[3], - data[0], - data[1], - data[4], - data[5], - ]); - - return ( - center != null && - data[2] < center[0] == center[0] < 0 && - data[3] < center[1] == center[1] < 0 && - data[4] < center[0] == center[0] < data[0] && - data[5] < center[1] == center[1] < data[1] - ); -} - -/** - * Computes lines equations by two points and returns their intersection point. - * - * @type {(coords: number[]) => undefined | Point} - */ -function getIntersection(coords) { - // Prev line equation parameters. - var a1 = coords[1] - coords[3], // y1 - y2 - b1 = coords[2] - coords[0], // x2 - x1 - c1 = coords[0] * coords[3] - coords[2] * coords[1], // x1 * y2 - x2 * y1 - // Next line equation parameters - a2 = coords[5] - coords[7], // y1 - y2 - b2 = coords[6] - coords[4], // x2 - x1 - c2 = coords[4] * coords[7] - coords[5] * coords[6], // x1 * y2 - x2 * y1 - denom = a1 * b2 - a2 * b1; - - if (!denom) return; // parallel lines haven't an intersection - - /** - * @type {Point} - */ - var cross = [(b1 * c2 - b2 * c1) / denom, (a1 * c2 - a2 * c1) / -denom]; - if ( - !isNaN(cross[0]) && - !isNaN(cross[1]) && - isFinite(cross[0]) && - isFinite(cross[1]) - ) { - return cross; - } -} - -/** - * Decrease accuracy of floating-point numbers - * in path data keeping a specified number of decimals. - * Smart rounds values like 2.3491 to 2.35 instead of 2.349. - * Doesn't apply "smartness" if the number precision fits already. - * - * @type {(data: number[]) => number[]} - */ -function strongRound(data) { - const precisionNum = precision || 0; - for (let i = data.length; i-- > 0; ) { - const fixed = toFixed(data[i], precisionNum); - if (fixed !== data[i]) { - const rounded = toFixed(data[i], precisionNum - 1); - data[i] = - toFixed(Math.abs(rounded - data[i]), precisionNum + 1) >= error - ? fixed - : rounded; - } - } - return data; -} - -/** - * Simple rounding function if precision is 0. - * - * @type {(data: number[]) => number[]} - */ -function round(data) { - for (var i = data.length; i-- > 0; ) { - data[i] = Math.round(data[i]); - } - return data; -} - -/** - * Checks if a curve is a straight line by measuring distance - * from middle points to the line formed by end points. - * - * @type {(data: number[]) => boolean} - */ -function isCurveStraightLine(data) { - // Get line equation a·x + b·y + c = 0 coefficients a, b (c = 0) by start and end points. - var i = data.length - 2, - a = -data[i + 1], // y1 − y2 (y1 = 0) - b = data[i], // x2 − x1 (x1 = 0) - d = 1 / (a * a + b * b); // same part for all points - - if (i <= 1 || !isFinite(d)) return false; // curve that ends at start point isn't the case - - // Distance from point (x0, y0) to the line is sqrt((c − a·x0 − b·y0)² / (a² + b²)) - while ((i -= 2) >= 0) { - if (Math.sqrt(Math.pow(a * data[i] + b * data[i + 1], 2) * d) > error) - return false; - } - - return true; -} - -/** - * Calculates the sagitta of an arc if possible. - * - * @type {(data: number[]) => number | undefined} - * @see https://wikipedia.org/wiki/Sagitta_(geometry)#Formulas - */ -function calculateSagitta(data) { - if (data[3] === 1) return undefined; - const [rx, ry] = data; - if (Math.abs(rx - ry) > error) return undefined; - const chord = Math.sqrt(data[5] ** 2 + data[6] ** 2); - if (chord > rx * 2) return undefined; - return rx - Math.sqrt(rx ** 2 - 0.25 * chord ** 2); -} - -/** - * Converts next curve from shorthand to full form using the current curve data. - * - * @type {(item: PathDataItem, data: number[]) => PathDataItem} - */ -function makeLonghand(item, data) { - switch (item.command) { - case 's': - item.command = 'c'; - break; - case 't': - item.command = 'q'; - break; - } - item.args.unshift( - data[data.length - 2] - data[data.length - 4], - data[data.length - 1] - data[data.length - 3], - ); - return item; -} - -/** - * Returns distance between two points - * - * @type {(point1: Point, point2: Point) => number} - */ -function getDistance(point1, point2) { - return Math.sqrt((point1[0] - point2[0]) ** 2 + (point1[1] - point2[1]) ** 2); -} - -/** - * Reflects point across another point. - * - * @param {Point} controlPoint - * @param {Point} base - * @returns {Point} - */ -function reflectPoint(controlPoint, base) { - return [2 * base[0] - controlPoint[0], 2 * base[1] - controlPoint[1]]; -} - -/** - * Returns coordinates of the curve point corresponding to the certain t - * a·(1 - t)³·p1 + b·(1 - t)²·t·p2 + c·(1 - t)·t²·p3 + d·t³·p4, - * where pN are control points and p1 is zero due to relative coordinates. - * - * @type {(curve: number[], t: number) => Point} - */ -function getCubicBezierPoint(curve, t) { - var sqrT = t * t, - cubT = sqrT * t, - mt = 1 - t, - sqrMt = mt * mt; - - return [ - 3 * sqrMt * t * curve[0] + 3 * mt * sqrT * curve[2] + cubT * curve[4], - 3 * sqrMt * t * curve[1] + 3 * mt * sqrT * curve[3] + cubT * curve[5], - ]; -} - -/** - * Finds circle by 3 points of the curve and checks if the curve fits the found circle. - * - * @type {(curve: number[]) => undefined | Circle} - */ -function findCircle(curve) { - var midPoint = getCubicBezierPoint(curve, 1 / 2), - m1 = [midPoint[0] / 2, midPoint[1] / 2], - m2 = [(midPoint[0] + curve[4]) / 2, (midPoint[1] + curve[5]) / 2], - center = getIntersection([ - m1[0], - m1[1], - m1[0] + m1[1], - m1[1] - m1[0], - m2[0], - m2[1], - m2[0] + (m2[1] - midPoint[1]), - m2[1] - (m2[0] - midPoint[0]), - ]), - radius = center && getDistance([0, 0], center), - // @ts-ignore - tolerance = Math.min(arcThreshold * error, (arcTolerance * radius) / 100); - - if ( - center && - // @ts-ignore - radius < 1e15 && - [1 / 4, 3 / 4].every(function (point) { - return ( - Math.abs( - // @ts-ignore - getDistance(getCubicBezierPoint(curve, point), center) - radius, - ) <= tolerance - ); - }) - ) - // @ts-ignore - return { center: center, radius: radius }; -} - -/** - * Checks if a curve fits the given circle. - * - * @type {(curve: number[], circle: Circle) => boolean} - */ -function isArc(curve, circle) { - var tolerance = Math.min( - arcThreshold * error, - (arcTolerance * circle.radius) / 100, - ); - - return [0, 1 / 4, 1 / 2, 3 / 4, 1].every(function (point) { - return ( - Math.abs( - getDistance(getCubicBezierPoint(curve, point), circle.center) - - circle.radius, - ) <= tolerance - ); - }); -} - -/** - * Checks if a previous curve fits the given circle. - * - * @type {(curve: number[], circle: Circle) => boolean} - */ -function isArcPrev(curve, circle) { - return isArc(curve, { - center: [circle.center[0] + curve[4], circle.center[1] + curve[5]], - radius: circle.radius, - }); -} - -/** - * Finds angle of a curve fitting the given arc. - - * @type {(curve: number[], relCircle: Circle) => number} - */ -function findArcAngle(curve, relCircle) { - var x1 = -relCircle.center[0], - y1 = -relCircle.center[1], - x2 = curve[4] - relCircle.center[0], - y2 = curve[5] - relCircle.center[1]; - - return Math.acos( - (x1 * x2 + y1 * y2) / Math.sqrt((x1 * x1 + y1 * y1) * (x2 * x2 + y2 * y2)), - ); -} - -/** - * Converts given path data to string. - * - * @type {(params: InternalParams, pathData: PathDataItem[]) => string} - */ -function data2Path(params, pathData) { - return pathData.reduce(function (pathString, item) { - var strData = ''; - if (item.args) { - strData = cleanupOutData(roundData(item.args.slice()), params); - } - return pathString + item.command + strData; - }, ''); -} diff --git a/node_modules/svgo/plugins/convertShapeToPath.js b/node_modules/svgo/plugins/convertShapeToPath.js deleted file mode 100644 index f0ba0fd8..00000000 --- a/node_modules/svgo/plugins/convertShapeToPath.js +++ /dev/null @@ -1,170 +0,0 @@ -'use strict'; - -/** - * @typedef {import('../lib/types').PathDataItem} PathDataItem - */ - -const { stringifyPathData } = require('../lib/path.js'); -const { detachNodeFromParent } = require('../lib/xast.js'); - -exports.name = 'convertShapeToPath'; -exports.description = 'converts basic shapes to more compact path form'; - -const regNumber = /[-+]?(?:\d*\.\d+|\d+\.?)(?:[eE][-+]?\d+)?/g; - -/** - * Converts basic shape to more compact path. - * It also allows further optimizations like - * combining paths with similar attributes. - * - * @see https://www.w3.org/TR/SVG11/shapes.html - * - * @author Lev Solntsev - * - * @type {import('./plugins-types').Plugin<'convertShapeToPath'>} - */ -exports.fn = (root, params) => { - const { convertArcs = false, floatPrecision: precision } = params; - - return { - element: { - enter: (node, parentNode) => { - // convert rect to path - if ( - node.name === 'rect' && - node.attributes.width != null && - node.attributes.height != null && - node.attributes.rx == null && - node.attributes.ry == null - ) { - const x = Number(node.attributes.x || '0'); - const y = Number(node.attributes.y || '0'); - const width = Number(node.attributes.width); - const height = Number(node.attributes.height); - // Values like '100%' compute to NaN, thus running after - // cleanupNumericValues when 'px' units has already been removed. - // TODO: Calculate sizes from % and non-px units if possible. - if (Number.isNaN(x - y + width - height)) return; - /** - * @type {PathDataItem[]} - */ - const pathData = [ - { command: 'M', args: [x, y] }, - { command: 'H', args: [x + width] }, - { command: 'V', args: [y + height] }, - { command: 'H', args: [x] }, - { command: 'z', args: [] }, - ]; - node.name = 'path'; - node.attributes.d = stringifyPathData({ pathData, precision }); - delete node.attributes.x; - delete node.attributes.y; - delete node.attributes.width; - delete node.attributes.height; - } - - // convert line to path - if (node.name === 'line') { - const x1 = Number(node.attributes.x1 || '0'); - const y1 = Number(node.attributes.y1 || '0'); - const x2 = Number(node.attributes.x2 || '0'); - const y2 = Number(node.attributes.y2 || '0'); - if (Number.isNaN(x1 - y1 + x2 - y2)) return; - /** - * @type {PathDataItem[]} - */ - const pathData = [ - { command: 'M', args: [x1, y1] }, - { command: 'L', args: [x2, y2] }, - ]; - node.name = 'path'; - node.attributes.d = stringifyPathData({ pathData, precision }); - delete node.attributes.x1; - delete node.attributes.y1; - delete node.attributes.x2; - delete node.attributes.y2; - } - - // convert polyline and polygon to path - if ( - (node.name === 'polyline' || node.name === 'polygon') && - node.attributes.points != null - ) { - const coords = (node.attributes.points.match(regNumber) || []).map( - Number, - ); - if (coords.length < 4) { - detachNodeFromParent(node, parentNode); - return; - } - /** - * @type {PathDataItem[]} - */ - const pathData = []; - for (let i = 0; i < coords.length; i += 2) { - pathData.push({ - command: i === 0 ? 'M' : 'L', - args: coords.slice(i, i + 2), - }); - } - if (node.name === 'polygon') { - pathData.push({ command: 'z', args: [] }); - } - node.name = 'path'; - node.attributes.d = stringifyPathData({ pathData, precision }); - delete node.attributes.points; - } - - // optionally convert circle - if (node.name === 'circle' && convertArcs) { - const cx = Number(node.attributes.cx || '0'); - const cy = Number(node.attributes.cy || '0'); - const r = Number(node.attributes.r || '0'); - if (Number.isNaN(cx - cy + r)) { - return; - } - /** - * @type {PathDataItem[]} - */ - const pathData = [ - { command: 'M', args: [cx, cy - r] }, - { command: 'A', args: [r, r, 0, 1, 0, cx, cy + r] }, - { command: 'A', args: [r, r, 0, 1, 0, cx, cy - r] }, - { command: 'z', args: [] }, - ]; - node.name = 'path'; - node.attributes.d = stringifyPathData({ pathData, precision }); - delete node.attributes.cx; - delete node.attributes.cy; - delete node.attributes.r; - } - - // optionally convert ellipse - if (node.name === 'ellipse' && convertArcs) { - const ecx = Number(node.attributes.cx || '0'); - const ecy = Number(node.attributes.cy || '0'); - const rx = Number(node.attributes.rx || '0'); - const ry = Number(node.attributes.ry || '0'); - if (Number.isNaN(ecx - ecy + rx - ry)) { - return; - } - /** - * @type {PathDataItem[]} - */ - const pathData = [ - { command: 'M', args: [ecx, ecy - ry] }, - { command: 'A', args: [rx, ry, 0, 1, 0, ecx, ecy + ry] }, - { command: 'A', args: [rx, ry, 0, 1, 0, ecx, ecy - ry] }, - { command: 'z', args: [] }, - ]; - node.name = 'path'; - node.attributes.d = stringifyPathData({ pathData, precision }); - delete node.attributes.cx; - delete node.attributes.cy; - delete node.attributes.rx; - delete node.attributes.ry; - } - }, - }, - }; -}; diff --git a/node_modules/svgo/plugins/convertStyleToAttrs.js b/node_modules/svgo/plugins/convertStyleToAttrs.js deleted file mode 100644 index a9305fc4..00000000 --- a/node_modules/svgo/plugins/convertStyleToAttrs.js +++ /dev/null @@ -1,136 +0,0 @@ -'use strict'; - -const { attrsGroups } = require('./_collections'); - -exports.name = 'convertStyleToAttrs'; -exports.description = 'converts style to attributes'; - -/** - * @type {(...args: string[]) => string} - */ -const g = (...args) => { - return '(?:' + args.join('|') + ')'; -}; - -const stylingProps = attrsGroups.presentation; -const rEscape = '\\\\(?:[0-9a-f]{1,6}\\s?|\\r\\n|.)'; // Like \" or \2051. Code points consume one space. -const rAttr = '\\s*(' + g('[^:;\\\\]', rEscape) + '*?)\\s*'; // attribute name like ‘fill’ -const rSingleQuotes = "'(?:[^'\\n\\r\\\\]|" + rEscape + ")*?(?:'|$)"; // string in single quotes: 'smth' -const rQuotes = '"(?:[^"\\n\\r\\\\]|' + rEscape + ')*?(?:"|$)'; // string in double quotes: "smth" -const rQuotedString = new RegExp('^' + g(rSingleQuotes, rQuotes) + '$'); -// Parentheses, E.g.: url(data:image/png;base64,iVBO...). -// ':' and ';' inside of it should be treated as is. (Just like in strings.) -const rParenthesis = - '\\(' + g('[^\'"()\\\\]+', rEscape, rSingleQuotes, rQuotes) + '*?' + '\\)'; -// The value. It can have strings and parentheses (see above). Fallbacks to anything in case of unexpected input. -const rValue = - '\\s*(' + - g( - '[^!\'"();\\\\]+?', - rEscape, - rSingleQuotes, - rQuotes, - rParenthesis, - '[^;]*?', - ) + - '*?' + - ')'; -// End of declaration. Spaces outside of capturing groups help to do natural trimming. -const rDeclEnd = '\\s*(?:;\\s*|$)'; -// Important rule -const rImportant = '(\\s*!important(?![-(\\w]))?'; -// Final RegExp to parse CSS declarations. -const regDeclarationBlock = new RegExp( - rAttr + ':' + rValue + rImportant + rDeclEnd, - 'ig', -); -// Comments expression. Honors escape sequences and strings. -const regStripComments = new RegExp( - g(rEscape, rSingleQuotes, rQuotes, '/\\*[^]*?\\*/'), - 'ig', -); - -/** - * Convert style in attributes. Cleanups comments and illegal declarations (without colon) as a side effect. - * - * @example - * <g style="fill:#000; color: #fff;"> - * ⬇ - * <g fill="#000" color="#fff"> - * - * @example - * <g style="fill:#000; color: #fff; -webkit-blah: blah"> - * ⬇ - * <g fill="#000" color="#fff" style="-webkit-blah: blah"> - * - * @author Kir Belevich - * - * @type {import('./plugins-types').Plugin<'convertStyleToAttrs'>} - */ -exports.fn = (_root, params) => { - const { keepImportant = false } = params; - return { - element: { - enter: (node) => { - if (node.attributes.style != null) { - // ['opacity: 1', 'color: #000'] - let styles = []; - /** - * @type {Record<string, string>} - */ - const newAttributes = {}; - - // Strip CSS comments preserving escape sequences and strings. - const styleValue = node.attributes.style.replace( - regStripComments, - (match) => { - return match[0] == '/' - ? '' - : match[0] == '\\' && /[-g-z]/i.test(match[1]) - ? match[1] - : match; - }, - ); - - regDeclarationBlock.lastIndex = 0; - for (var rule; (rule = regDeclarationBlock.exec(styleValue)); ) { - if (!keepImportant || !rule[3]) { - styles.push([rule[1], rule[2]]); - } - } - - if (styles.length) { - styles = styles.filter(function (style) { - if (style[0]) { - var prop = style[0].toLowerCase(), - val = style[1]; - - if (rQuotedString.test(val)) { - val = val.slice(1, -1); - } - - if (stylingProps.has(prop)) { - newAttributes[prop] = val; - - return false; - } - } - - return true; - }); - - Object.assign(node.attributes, newAttributes); - - if (styles.length) { - node.attributes.style = styles - .map((declaration) => declaration.join(':')) - .join(';'); - } else { - delete node.attributes.style; - } - } - } - }, - }, - }; -}; diff --git a/node_modules/svgo/plugins/convertTransform.js b/node_modules/svgo/plugins/convertTransform.js deleted file mode 100644 index 6f399078..00000000 --- a/node_modules/svgo/plugins/convertTransform.js +++ /dev/null @@ -1,418 +0,0 @@ -'use strict'; - -/** - * @typedef {import('../lib/types').XastChild} XastChild - * @typedef {import('../lib/types').XastElement} XastElement - * @typedef {import('../lib/types').XastParent} XastParent - */ - -const { cleanupOutData, toFixed } = require('../lib/svgo/tools.js'); -const { - transform2js, - transformsMultiply, - matrixToTransform, -} = require('./_transforms.js'); - -exports.name = 'convertTransform'; -exports.description = 'collapses multiple transformations and optimizes it'; - -/** - * Convert matrices to the short aliases, - * convert long translate, scale or rotate transform notations to the shorts ones, - * convert transforms to the matrices and multiply them all into one, - * remove useless transforms. - * - * @see https://www.w3.org/TR/SVG11/coords.html#TransformMatrixDefined - * - * @author Kir Belevich - * - * @type {import('./plugins-types').Plugin<'convertTransform'>} - */ -exports.fn = (_root, params) => { - const { - convertToShorts = true, - // degPrecision = 3, // transformPrecision (or matrix precision) - 2 by default - degPrecision, - floatPrecision = 3, - transformPrecision = 5, - matrixToTransform = true, - shortTranslate = true, - shortScale = true, - shortRotate = true, - removeUseless = true, - collapseIntoOne = true, - leadingZero = true, - negativeExtraSpace = false, - } = params; - const newParams = { - convertToShorts, - degPrecision, - floatPrecision, - transformPrecision, - matrixToTransform, - shortTranslate, - shortScale, - shortRotate, - removeUseless, - collapseIntoOne, - leadingZero, - negativeExtraSpace, - }; - return { - element: { - enter: (node) => { - if (node.attributes.transform != null) { - convertTransform(node, 'transform', newParams); - } - - if (node.attributes.gradientTransform != null) { - convertTransform(node, 'gradientTransform', newParams); - } - - if (node.attributes.patternTransform != null) { - convertTransform(node, 'patternTransform', newParams); - } - }, - }, - }; -}; - -/** - * @typedef {{ - * convertToShorts: boolean, - * degPrecision?: number, - * floatPrecision: number, - * transformPrecision: number, - * matrixToTransform: boolean, - * shortTranslate: boolean, - * shortScale: boolean, - * shortRotate: boolean, - * removeUseless: boolean, - * collapseIntoOne: boolean, - * leadingZero: boolean, - * negativeExtraSpace: boolean, - * }} TransformParams - */ - -/** - * @typedef {{ name: string, data: number[] }} TransformItem - */ - -/** - * @param {XastElement} item - * @param {string} attrName - * @param {TransformParams} params - */ -const convertTransform = (item, attrName, params) => { - let data = transform2js(item.attributes[attrName]); - params = definePrecision(data, params); - - if (params.collapseIntoOne && data.length > 1) { - data = [transformsMultiply(data)]; - } - - if (params.convertToShorts) { - data = convertToShorts(data, params); - } else { - data.forEach((item) => roundTransform(item, params)); - } - - if (params.removeUseless) { - data = removeUseless(data); - } - - if (data.length) { - item.attributes[attrName] = js2transform(data, params); - } else { - delete item.attributes[attrName]; - } -}; - -/** - * Defines precision to work with certain parts. - * transformPrecision - for scale and four first matrix parameters (needs a better precision due to multiplying), - * floatPrecision - for translate including two last matrix and rotate parameters, - * degPrecision - for rotate and skew. By default it's equal to (roughly) - * transformPrecision - 2 or floatPrecision whichever is lower. Can be set in params. - * - * @type {(data: TransformItem[], params: TransformParams) => TransformParams} - * - * clone params so it don't affect other elements transformations. - */ -const definePrecision = (data, { ...newParams }) => { - const matrixData = []; - for (const item of data) { - if (item.name == 'matrix') { - matrixData.push(...item.data.slice(0, 4)); - } - } - let numberOfDigits = newParams.transformPrecision; - // Limit transform precision with matrix one. Calculating with larger precision doesn't add any value. - if (matrixData.length) { - newParams.transformPrecision = Math.min( - newParams.transformPrecision, - Math.max.apply(Math, matrixData.map(floatDigits)) || - newParams.transformPrecision, - ); - numberOfDigits = Math.max.apply( - Math, - matrixData.map( - (n) => n.toString().replace(/\D+/g, '').length, // Number of digits in a number. 123.45 → 5 - ), - ); - } - // No sense in angle precision more then number of significant digits in matrix. - if (newParams.degPrecision == null) { - newParams.degPrecision = Math.max( - 0, - Math.min(newParams.floatPrecision, numberOfDigits - 2), - ); - } - return newParams; -}; - -/** - * @type {(data: number[], params: TransformParams) => number[]} - */ -const degRound = (data, params) => { - if ( - params.degPrecision != null && - params.degPrecision >= 1 && - params.floatPrecision < 20 - ) { - return smartRound(params.degPrecision, data); - } else { - return round(data); - } -}; -/** - * @type {(data: number[], params: TransformParams) => number[]} - */ -const floatRound = (data, params) => { - if (params.floatPrecision >= 1 && params.floatPrecision < 20) { - return smartRound(params.floatPrecision, data); - } else { - return round(data); - } -}; - -/** - * @type {(data: number[], params: TransformParams) => number[]} - */ -const transformRound = (data, params) => { - if (params.transformPrecision >= 1 && params.floatPrecision < 20) { - return smartRound(params.transformPrecision, data); - } else { - return round(data); - } -}; - -/** - * Returns number of digits after the point. 0.125 → 3 - * - * @type {(n: number) => number} - */ -const floatDigits = (n) => { - const str = n.toString(); - return str.slice(str.indexOf('.')).length - 1; -}; - -/** - * Convert transforms to the shorthand alternatives. - * - * @param {TransformItem[]} transforms - * @param {TransformParams} params - * @returns {TransformItem[]} - */ -const convertToShorts = (transforms, params) => { - for (var i = 0; i < transforms.length; i++) { - let transform = transforms[i]; - - // convert matrix to the short aliases - if (params.matrixToTransform && transform.name === 'matrix') { - var decomposed = matrixToTransform(transform, params); - if ( - js2transform(decomposed, params).length <= - js2transform([transform], params).length - ) { - transforms.splice(i, 1, ...decomposed); - } - transform = transforms[i]; - } - - // fixed-point numbers - // 12.754997 → 12.755 - roundTransform(transform, params); - - // convert long translate transform notation to the shorts one - // translate(10 0) → translate(10) - if ( - params.shortTranslate && - transform.name === 'translate' && - transform.data.length === 2 && - !transform.data[1] - ) { - transform.data.pop(); - } - - // convert long scale transform notation to the shorts one - // scale(2 2) → scale(2) - if ( - params.shortScale && - transform.name === 'scale' && - transform.data.length === 2 && - transform.data[0] === transform.data[1] - ) { - transform.data.pop(); - } - - // convert long rotate transform notation to the short one - // translate(cx cy) rotate(a) translate(-cx -cy) → rotate(a cx cy) - if ( - params.shortRotate && - transforms[i - 2]?.name === 'translate' && - transforms[i - 1].name === 'rotate' && - transforms[i].name === 'translate' && - transforms[i - 2].data[0] === -transforms[i].data[0] && - transforms[i - 2].data[1] === -transforms[i].data[1] - ) { - transforms.splice(i - 2, 3, { - name: 'rotate', - data: [ - transforms[i - 1].data[0], - transforms[i - 2].data[0], - transforms[i - 2].data[1], - ], - }); - - // splice compensation - i -= 2; - } - } - - return transforms; -}; - -/** - * Remove useless transforms. - * - * @type {(transforms: TransformItem[]) => TransformItem[]} - */ -const removeUseless = (transforms) => { - return transforms.filter((transform) => { - // translate(0), rotate(0[, cx, cy]), skewX(0), skewY(0) - if ( - (['translate', 'rotate', 'skewX', 'skewY'].indexOf(transform.name) > -1 && - (transform.data.length == 1 || transform.name == 'rotate') && - !transform.data[0]) || - // translate(0, 0) - (transform.name == 'translate' && - !transform.data[0] && - !transform.data[1]) || - // scale(1) - (transform.name == 'scale' && - transform.data[0] == 1 && - (transform.data.length < 2 || transform.data[1] == 1)) || - // matrix(1 0 0 1 0 0) - (transform.name == 'matrix' && - transform.data[0] == 1 && - transform.data[3] == 1 && - !( - transform.data[1] || - transform.data[2] || - transform.data[4] || - transform.data[5] - )) - ) { - return false; - } - - return true; - }); -}; - -/** - * Convert transforms JS representation to string. - * - * @param {TransformItem[]} transformJS - * @param {TransformParams} params - * @returns {string} - */ -const js2transform = (transformJS, params) => { - const transformString = transformJS - .map((transform) => { - roundTransform(transform, params); - return `${transform.name}(${cleanupOutData(transform.data, params)})`; - }) - .join(''); - - return transformString; -}; - -/** - * @type {(transform: TransformItem, params: TransformParams) => TransformItem} - */ -const roundTransform = (transform, params) => { - switch (transform.name) { - case 'translate': - transform.data = floatRound(transform.data, params); - break; - case 'rotate': - transform.data = [ - ...degRound(transform.data.slice(0, 1), params), - ...floatRound(transform.data.slice(1), params), - ]; - break; - case 'skewX': - case 'skewY': - transform.data = degRound(transform.data, params); - break; - case 'scale': - transform.data = transformRound(transform.data, params); - break; - case 'matrix': - transform.data = [ - ...transformRound(transform.data.slice(0, 4), params), - ...floatRound(transform.data.slice(4), params), - ]; - break; - } - return transform; -}; - -/** - * Rounds numbers in array. - * - * @type {(data: number[]) => number[]} - */ -const round = (data) => { - return data.map(Math.round); -}; - -/** - * Decrease accuracy of floating-point numbers - * in transforms keeping a specified number of decimals. - * Smart rounds values like 2.349 to 2.35. - * - * @param {number} precision - * @param {number[]} data - * @returns {number[]} - */ -const smartRound = (precision, data) => { - for ( - var i = data.length, - tolerance = +Math.pow(0.1, precision).toFixed(precision); - i--; - - ) { - if (toFixed(data[i], precision) !== data[i]) { - var rounded = +data[i].toFixed(precision - 1); - data[i] = - +Math.abs(rounded - data[i]).toFixed(precision + 1) >= tolerance - ? +data[i].toFixed(precision) - : rounded; - } - } - - return data; -}; diff --git a/node_modules/svgo/plugins/inlineStyles.js b/node_modules/svgo/plugins/inlineStyles.js deleted file mode 100644 index e09988de..00000000 --- a/node_modules/svgo/plugins/inlineStyles.js +++ /dev/null @@ -1,390 +0,0 @@ -'use strict'; - -/** - * @typedef {import('../lib/types').XastElement} XastElement - * @typedef {import('../lib/types').XastParent} XastParent - */ - -const csstree = require('css-tree'); -const { - syntax: { specificity }, -} = require('csso'); -const { - visitSkip, - querySelectorAll, - detachNodeFromParent, -} = require('../lib/xast.js'); -const { compareSpecificity, includesAttrSelector } = require('../lib/style'); -const { attrsGroups, pseudoClasses } = require('./_collections'); - -exports.name = 'inlineStyles'; -exports.description = 'inline styles (additional options)'; - -/** - * Some pseudo-classes can only be calculated by clients, like :visited, - * :future, or :hover, but there are other pseudo-classes that we can evaluate - * during optimization. - * - * The list of pseudo-classes that we can evaluate during optimization, and so - * shouldn't be toggled conditionally through the `usePseudos` parameter. - * - * @see https://developer.mozilla.org/docs/Web/CSS/Pseudo-classes - */ -const preservedPseudos = [ - ...pseudoClasses.functional, - ...pseudoClasses.treeStructural, -]; - -/** - * Merges styles from style nodes into inline styles. - * - * @type {import('./plugins-types').Plugin<'inlineStyles'>} - * @author strarsis <strarsis@gmail.com> - */ -exports.fn = (root, params) => { - const { - onlyMatchedOnce = true, - removeMatchedSelectors = true, - useMqs = ['', 'screen'], - usePseudos = [''], - } = params; - - /** - * @type {{ node: XastElement, parentNode: XastParent, cssAst: csstree.StyleSheet }[]} - */ - const styles = []; - /** - * @type {{ - * node: csstree.Selector, - * item: csstree.ListItem<csstree.CssNode>, - * rule: csstree.Rule, - * matchedElements?: XastElement[] - * }[]} - */ - let selectors = []; - - return { - element: { - enter: (node, parentNode) => { - if (node.name === 'foreignObject') { - return visitSkip; - } - if (node.name !== 'style' || node.children.length === 0) { - return; - } - if ( - node.attributes.type != null && - node.attributes.type !== '' && - node.attributes.type !== 'text/css' - ) { - return; - } - - const cssText = node.children - .filter((child) => child.type === 'text' || child.type === 'cdata') - // @ts-ignore - .map((child) => child.value) - .join(''); - - /** @type {?csstree.CssNode} */ - let cssAst = null; - try { - cssAst = csstree.parse(cssText, { - parseValue: false, - parseCustomProperty: false, - }); - } catch { - return; - } - if (cssAst.type === 'StyleSheet') { - styles.push({ node, parentNode, cssAst }); - } - - // collect selectors - csstree.walk(cssAst, { - visit: 'Rule', - enter(node) { - const atrule = this.atrule; - - // skip media queries not included into useMqs param - let mediaQuery = ''; - if (atrule != null) { - mediaQuery = atrule.name; - if (atrule.prelude != null) { - mediaQuery += ` ${csstree.generate(atrule.prelude)}`; - } - } - if (!useMqs.includes(mediaQuery)) { - return; - } - - if (node.prelude.type === 'SelectorList') { - node.prelude.children.forEach((childNode, item) => { - if (childNode.type === 'Selector') { - /** - * @type {{ - * item: csstree.ListItem<csstree.CssNode>, - * list: csstree.List<csstree.CssNode> - * }[]} - */ - const pseudos = []; - - childNode.children.forEach( - (grandchildNode, grandchildItem, grandchildList) => { - const isPseudo = - grandchildNode.type === 'PseudoClassSelector' || - grandchildNode.type === 'PseudoElementSelector'; - - if ( - isPseudo && - !preservedPseudos.includes(grandchildNode.name) - ) { - pseudos.push({ - item: grandchildItem, - list: grandchildList, - }); - } - }, - ); - - const pseudoSelectors = csstree.generate({ - type: 'Selector', - children: new csstree.List().fromArray( - pseudos.map((pseudo) => pseudo.item.data), - ), - }); - - if (usePseudos.includes(pseudoSelectors)) { - for (const pseudo of pseudos) { - pseudo.list.remove(pseudo.item); - } - } - - selectors.push({ node: childNode, rule: node, item: item }); - } - }); - } - }, - }); - }, - }, - - root: { - exit: () => { - if (styles.length === 0) { - return; - } - const sortedSelectors = selectors - .slice() - .sort((a, b) => { - const aSpecificity = specificity(a.item.data); - const bSpecificity = specificity(b.item.data); - return compareSpecificity(aSpecificity, bSpecificity); - }) - .reverse(); - - for (const selector of sortedSelectors) { - // match selectors - const selectorText = csstree.generate(selector.item.data); - /** @type {XastElement[]} */ - const matchedElements = []; - try { - for (const node of querySelectorAll(root, selectorText)) { - if (node.type === 'element') { - matchedElements.push(node); - } - } - } catch (selectError) { - continue; - } - // nothing selected - if (matchedElements.length === 0) { - continue; - } - - // apply styles to matched elements - // skip selectors that match more than once if option onlyMatchedOnce is enabled - if (onlyMatchedOnce && matchedElements.length > 1) { - continue; - } - - // apply <style/> to matched elements - for (const selectedEl of matchedElements) { - const styleDeclarationList = csstree.parse( - selectedEl.attributes.style ?? '', - { - context: 'declarationList', - parseValue: false, - }, - ); - if (styleDeclarationList.type !== 'DeclarationList') { - continue; - } - const styleDeclarationItems = new Map(); - - /** @type {csstree.ListItem<csstree.CssNode>} */ - let firstListItem; - - csstree.walk(styleDeclarationList, { - visit: 'Declaration', - enter(node, item) { - if (firstListItem == null) { - firstListItem = item; - } - - styleDeclarationItems.set(node.property.toLowerCase(), item); - }, - }); - // merge declarations - csstree.walk(selector.rule, { - visit: 'Declaration', - enter(ruleDeclaration) { - // existing inline styles have higher priority - // no inline styles, external styles, external styles used - // inline styles, external styles same priority as inline styles, inline styles used - // inline styles, external styles higher priority than inline styles, external styles used - const property = ruleDeclaration.property; - - if ( - attrsGroups.presentation.has(property) && - !selectors.some((selector) => - includesAttrSelector(selector.item, property), - ) - ) { - delete selectedEl.attributes[property]; - } - - const matchedItem = styleDeclarationItems.get(property); - const ruleDeclarationItem = - styleDeclarationList.children.createItem(ruleDeclaration); - if (matchedItem == null) { - styleDeclarationList.children.insert( - ruleDeclarationItem, - firstListItem, - ); - } else if ( - matchedItem.data.important !== true && - ruleDeclaration.important === true - ) { - styleDeclarationList.children.replace( - matchedItem, - ruleDeclarationItem, - ); - styleDeclarationItems.set(property, ruleDeclarationItem); - } - }, - }); - - const newStyles = csstree.generate(styleDeclarationList); - if (newStyles.length !== 0) { - selectedEl.attributes.style = newStyles; - } - } - - if ( - removeMatchedSelectors && - matchedElements.length !== 0 && - selector.rule.prelude.type === 'SelectorList' - ) { - // clean up matching simple selectors if option removeMatchedSelectors is enabled - selector.rule.prelude.children.remove(selector.item); - } - selector.matchedElements = matchedElements; - } - - // no further processing required - if (!removeMatchedSelectors) { - return; - } - - // clean up matched class + ID attribute values - for (const selector of sortedSelectors) { - if (selector.matchedElements == null) { - continue; - } - - if (onlyMatchedOnce && selector.matchedElements.length > 1) { - // skip selectors that match more than once if option onlyMatchedOnce is enabled - continue; - } - - for (const selectedEl of selector.matchedElements) { - // class - const classList = new Set( - selectedEl.attributes.class == null - ? null - : selectedEl.attributes.class.split(' '), - ); - - for (const child of selector.node.children) { - if ( - child.type === 'ClassSelector' && - !selectors.some((selector) => - includesAttrSelector( - selector.item, - 'class', - child.name, - true, - ), - ) - ) { - classList.delete(child.name); - } - } - - if (classList.size === 0) { - delete selectedEl.attributes.class; - } else { - selectedEl.attributes.class = Array.from(classList).join(' '); - } - - // ID - const firstSubSelector = selector.node.children.first; - if ( - firstSubSelector?.type === 'IdSelector' && - selectedEl.attributes.id === firstSubSelector.name && - !selectors.some((selector) => - includesAttrSelector( - selector.item, - 'id', - firstSubSelector.name, - true, - ), - ) - ) { - delete selectedEl.attributes.id; - } - } - } - - for (const style of styles) { - csstree.walk(style.cssAst, { - visit: 'Rule', - enter: function (node, item, list) { - // clean up <style/> rulesets without any css selectors left - if ( - node.type === 'Rule' && - node.prelude.type === 'SelectorList' && - node.prelude.children.isEmpty - ) { - list.remove(item); - } - }, - }); - - // csstree v2 changed this type - if (style.cssAst.children.isEmpty) { - // remove empty style element - detachNodeFromParent(style.node, style.parentNode); - } else { - // update style element if any styles left - const firstChild = style.node.children[0]; - if (firstChild.type === 'text' || firstChild.type === 'cdata') { - firstChild.value = csstree.generate(style.cssAst); - } - } - } - }, - }, - }; -}; diff --git a/node_modules/svgo/plugins/mergePaths.js b/node_modules/svgo/plugins/mergePaths.js deleted file mode 100644 index fe982c13..00000000 --- a/node_modules/svgo/plugins/mergePaths.js +++ /dev/null @@ -1,148 +0,0 @@ -'use strict'; - -/** - * @typedef {import("../lib/types").PathDataItem} PathDataItem - * @typedef {import('../lib/types').XastChild} XastChild - * @typedef {import('../lib/types').XastElement} XastElement - */ - -const { collectStylesheet, computeStyle } = require('../lib/style.js'); -const { path2js, js2path, intersects } = require('./_path.js'); - -exports.name = 'mergePaths'; -exports.description = 'merges multiple paths in one if possible'; - -/** - * Merge multiple Paths into one. - * - * @author Kir Belevich, Lev Solntsev - * - * @type {import('./plugins-types').Plugin<'mergePaths'>} - */ -exports.fn = (root, params) => { - const { - force = false, - floatPrecision, - noSpaceAfterFlags = false, // a20 60 45 0 1 30 20 → a20 60 45 0130 20 - } = params; - const stylesheet = collectStylesheet(root); - - return { - element: { - enter: (node) => { - if (node.children.length <= 1) { - return; - } - - /** @type {XastChild[]} */ - const elementsToRemove = []; - let prevChild = node.children[0]; - let prevPathData = null; - - /** - * @param {XastElement} child - * @param {PathDataItem[]} pathData - */ - const updatePreviousPath = (child, pathData) => { - js2path(child, pathData, { - floatPrecision, - noSpaceAfterFlags, - }); - prevPathData = null; - }; - - for (let i = 1; i < node.children.length; i++) { - const child = node.children[i]; - - if ( - prevChild.type !== 'element' || - prevChild.name !== 'path' || - prevChild.children.length !== 0 || - prevChild.attributes.d == null - ) { - if (prevPathData && prevChild.type === 'element') { - updatePreviousPath(prevChild, prevPathData); - } - prevChild = child; - continue; - } - - if ( - child.type !== 'element' || - child.name !== 'path' || - child.children.length !== 0 || - child.attributes.d == null - ) { - if (prevPathData) { - updatePreviousPath(prevChild, prevPathData); - } - prevChild = child; - continue; - } - - const computedStyle = computeStyle(stylesheet, child); - if ( - computedStyle['marker-start'] || - computedStyle['marker-mid'] || - computedStyle['marker-end'] - ) { - if (prevPathData) { - updatePreviousPath(prevChild, prevPathData); - } - prevChild = child; - continue; - } - const childAttrs = Object.keys(child.attributes); - if (childAttrs.length !== Object.keys(prevChild.attributes).length) { - if (prevPathData) { - updatePreviousPath(prevChild, prevPathData); - } - prevChild = child; - continue; - } - - const areAttrsEqual = childAttrs.some((attr) => { - return ( - attr !== 'd' && - prevChild.type === 'element' && - prevChild.attributes[attr] !== child.attributes[attr] - ); - }); - - if (areAttrsEqual) { - if (prevPathData) { - updatePreviousPath(prevChild, prevPathData); - } - prevChild = child; - continue; - } - - const hasPrevPath = prevPathData != null; - const currentPathData = path2js(child); - prevPathData = prevPathData ?? path2js(prevChild); - - if (force || !intersects(prevPathData, currentPathData)) { - prevPathData.push(...currentPathData); - elementsToRemove.push(child); - continue; - } - - if (hasPrevPath) { - updatePreviousPath(prevChild, prevPathData); - } - - prevChild = child; - prevPathData = null; - } - - if (prevPathData && prevChild.type === 'element') { - updatePreviousPath(prevChild, prevPathData); - } - - node.children = node.children.filter( - (child) => !elementsToRemove.includes(child), - ); - }, - }, - }; -}; diff --git a/node_modules/svgo/plugins/mergeStyles.js b/node_modules/svgo/plugins/mergeStyles.js deleted file mode 100644 index 764d0194..00000000 --- a/node_modules/svgo/plugins/mergeStyles.js +++ /dev/null @@ -1,98 +0,0 @@ -'use strict'; - -/** - * @typedef {import('../lib/types').XastElement} XastElement - * @typedef {import('../lib/types').XastChild} XastChild - */ - -const { visitSkip, detachNodeFromParent } = require('../lib/xast.js'); - -exports.name = 'mergeStyles'; -exports.description = 'merge multiple style elements into one'; - -/** - * Merge multiple style elements into one. - * - * @author strarsis <strarsis@gmail.com> - * - * @type {import('./plugins-types').Plugin<'mergeStyles'>} - */ -exports.fn = () => { - /** - * @type {?XastElement} - */ - let firstStyleElement = null; - let collectedStyles = ''; - /** - * @type {'text' | 'cdata'} - */ - let styleContentType = 'text'; - - return { - element: { - enter: (node, parentNode) => { - // skip <foreignObject> content - if (node.name === 'foreignObject') { - return visitSkip; - } - - // collect style elements - if (node.name !== 'style') { - return; - } - - // skip <style> with invalid type attribute - if ( - node.attributes.type != null && - node.attributes.type !== '' && - node.attributes.type !== 'text/css' - ) { - return; - } - - // extract style element content - let css = ''; - for (const child of node.children) { - if (child.type === 'text') { - css += child.value; - } - if (child.type === 'cdata') { - styleContentType = 'cdata'; - css += child.value; - } - } - - // remove empty style elements - if (css.trim().length === 0) { - detachNodeFromParent(node, parentNode); - return; - } - - // collect css and wrap with media query if present in attribute - if (node.attributes.media == null) { - collectedStyles += css; - } else { - collectedStyles += `@media ${node.attributes.media}{${css}}`; - delete node.attributes.media; - } - - // combine collected styles in the first style element - if (firstStyleElement == null) { - firstStyleElement = node; - } else { - detachNodeFromParent(node, parentNode); - /** - * @type {XastChild} - */ - const child = { type: styleContentType, value: collectedStyles }; - // TODO remove legacy parentNode in v4 - Object.defineProperty(child, 'parentNode', { - writable: true, - value: firstStyleElement, - }); - firstStyleElement.children = [child]; - } - }, - }, - }; -}; diff --git a/node_modules/svgo/plugins/minifyStyles.js b/node_modules/svgo/plugins/minifyStyles.js deleted file mode 100644 index ade441b3..00000000 --- a/node_modules/svgo/plugins/minifyStyles.js +++ /dev/null @@ -1,141 +0,0 @@ -'use strict'; - -/** - * @typedef {import('../lib/types').XastElement} XastElement - * @typedef {import('../lib/types').XastParent} XastParent - */ - -const csso = require('csso'); -const { detachNodeFromParent } = require('../lib/xast'); -const { hasScripts } = require('../lib/svgo/tools'); - -exports.name = 'minifyStyles'; -exports.description = 'minifies styles and removes unused styles'; - -/** - * Minifies styles (<style> element + style attribute) using CSSO. - * - * @author strarsis <strarsis@gmail.com> - * @type {import('./plugins-types').Plugin<'minifyStyles'>} - */ -exports.fn = (_root, { usage, ...params }) => { - /** @type {Map<XastElement, XastParent>} */ - const styleElements = new Map(); - - /** @type {XastElement[]} */ - const elementsWithStyleAttributes = []; - - /** @type {Set<string>} */ - const tagsUsage = new Set(); - - /** @type {Set<string>} */ - const idsUsage = new Set(); - - /** @type {Set<string>} */ - const classesUsage = new Set(); - - let enableTagsUsage = true; - let enableIdsUsage = true; - let enableClassesUsage = true; - - /** - * Force to use usage data even if it unsafe. For example, the document - * contains scripts or in attributes.. - */ - let forceUsageDeoptimized = false; - - if (typeof usage === 'boolean') { - enableTagsUsage = usage; - enableIdsUsage = usage; - enableClassesUsage = usage; - } else if (usage) { - enableTagsUsage = usage.tags == null ? true : usage.tags; - enableIdsUsage = usage.ids == null ? true : usage.ids; - enableClassesUsage = usage.classes == null ? true : usage.classes; - forceUsageDeoptimized = usage.force == null ? false : usage.force; - } - - let deoptimized = false; - - return { - element: { - enter: (node, parentNode) => { - // detect deoptimisations - if (hasScripts(node)) { - deoptimized = true; - } - - // collect tags, ids and classes usage - tagsUsage.add(node.name); - if (node.attributes.id != null) { - idsUsage.add(node.attributes.id); - } - if (node.attributes.class != null) { - for (const className of node.attributes.class.split(/\s+/)) { - classesUsage.add(className); - } - } - // collect style elements or elements with style attribute - if (node.name === 'style' && node.children.length !== 0) { - styleElements.set(node, parentNode); - } else if (node.attributes.style != null) { - elementsWithStyleAttributes.push(node); - } - }, - }, - - root: { - exit: () => { - /** @type {csso.Usage} */ - const cssoUsage = {}; - if (!deoptimized || forceUsageDeoptimized) { - if (enableTagsUsage) { - cssoUsage.tags = Array.from(tagsUsage); - } - if (enableIdsUsage) { - cssoUsage.ids = Array.from(idsUsage); - } - if (enableClassesUsage) { - cssoUsage.classes = Array.from(classesUsage); - } - } - // minify style elements - for (const [styleNode, styleNodeParent] of styleElements.entries()) { - if ( - styleNode.children[0].type === 'text' || - styleNode.children[0].type === 'cdata' - ) { - const cssText = styleNode.children[0].value; - const minified = csso.minify(cssText, { - ...params, - usage: cssoUsage, - }).css; - - if (minified.length === 0) { - detachNodeFromParent(styleNode, styleNodeParent); - continue; - } - - // preserve cdata if necessary - // TODO split cdata -> text optimisation into separate plugin - if (cssText.indexOf('>') >= 0 || cssText.indexOf('<') >= 0) { - styleNode.children[0].type = 'cdata'; - styleNode.children[0].value = minified; - } else { - styleNode.children[0].type = 'text'; - styleNode.children[0].value = minified; - } - } - } - // minify style attributes - for (const node of elementsWithStyleAttributes) { - // style attribute - const elemStyle = node.attributes.style; - node.attributes.style = csso.minifyBlock(elemStyle, { - ...params, - }).css; - } - }, - }, - }; -}; diff --git a/node_modules/svgo/plugins/moveElemsAttrsToGroup.js b/node_modules/svgo/plugins/moveElemsAttrsToGroup.js deleted file mode 100644 index 633f4439..00000000 --- a/node_modules/svgo/plugins/moveElemsAttrsToGroup.js +++ /dev/null @@ -1,128 +0,0 @@ -'use strict'; - -const { visit } = require('../lib/xast.js'); -const { inheritableAttrs, pathElems } = require('./_collections.js'); - -exports.name = 'moveElemsAttrsToGroup'; -exports.description = 'Move common attributes of group children to the group'; - -/** - * Move common attributes of group children to the group - * - * @example - * <g attr1="val1"> - * <g attr2="val2"> - * text - * </g> - * <circle attr2="val2" attr3="val3"/> - * </g> - * ⬇ - * <g attr1="val1" attr2="val2"> - * <g> - * text - * </g> - * <circle attr3="val3"/> - * </g> - * - * @author Kir Belevich - * - * @type {import('./plugins-types').Plugin<'moveElemsAttrsToGroup'>} - */ -exports.fn = (root) => { - // find if any style element is present - let deoptimizedWithStyles = false; - visit(root, { - element: { - enter: (node) => { - if (node.name === 'style') { - deoptimizedWithStyles = true; - } - }, - }, - }); - - return { - element: { - exit: (node) => { - // process only groups with more than 1 children - if (node.name !== 'g' || node.children.length <= 1) { - return; - } - - // deoptimize the plugin when style elements are present - // selectors may rely on id, classes or tag names - if (deoptimizedWithStyles) { - return; - } - - /** - * find common attributes in group children - * @type {Map<string, string>} - */ - const commonAttributes = new Map(); - let initial = true; - let everyChildIsPath = true; - for (const child of node.children) { - if (child.type === 'element') { - if (!pathElems.has(child.name)) { - everyChildIsPath = false; - } - if (initial) { - initial = false; - // collect all inheritable attributes from first child element - for (const [name, value] of Object.entries(child.attributes)) { - // consider only inheritable attributes - if (inheritableAttrs.has(name)) { - commonAttributes.set(name, value); - } - } - } else { - // exclude uncommon attributes from initial list - for (const [name, value] of commonAttributes) { - if (child.attributes[name] !== value) { - commonAttributes.delete(name); - } - } - } - } - } - - // preserve transform on children when group has clip-path or mask - if ( - node.attributes['clip-path'] != null || - node.attributes.mask != null - ) { - commonAttributes.delete('transform'); - } - - // preserve transform when all children are paths - // so the transform could be applied to path data by other plugins - if (everyChildIsPath) { - commonAttributes.delete('transform'); - } - - // add common children attributes to group - for (const [name, value] of commonAttributes) { - if (name === 'transform') { - if (node.attributes.transform != null) { - node.attributes.transform = `${node.attributes.transform} ${value}`; - } else { - node.attributes.transform = value; - } - } else { - node.attributes[name] = value; - } - } - - // delete common attributes from children - for (const child of node.children) { - if (child.type === 'element') { - for (const [name] of commonAttributes) { - delete child.attributes[name]; - } - } - } - }, - }, - }; -}; diff --git a/node_modules/svgo/plugins/moveGroupAttrsToElems.js b/node_modules/svgo/plugins/moveGroupAttrsToElems.js deleted file mode 100644 index 450fb265..00000000 --- a/node_modules/svgo/plugins/moveGroupAttrsToElems.js +++ /dev/null @@ -1,65 +0,0 @@ -'use strict'; - -const { pathElems, referencesProps } = require('./_collections.js'); -const { includesUrlReference } = require('../lib/svgo/tools.js'); - -exports.name = 'moveGroupAttrsToElems'; -exports.description = 'moves some group attributes to the content elements'; - -const pathElemsWithGroupsAndText = [...pathElems, 'g', 'text']; - -/** - * Move group attrs to the content elements. - * - * @example - * <g transform="scale(2)"> - * <path transform="rotate(45)" d="M0,0 L10,20"/> - * <path transform="translate(10, 20)" d="M0,10 L20,30"/> - * </g> - * ⬇ - * <g> - * <path transform="scale(2) rotate(45)" d="M0,0 L10,20"/> - * <path transform="scale(2) translate(10, 20)" d="M0,10 L20,30"/> - * </g> - * - * @author Kir Belevich - * - * @type {import('./plugins-types').Plugin<'moveGroupAttrsToElems'>} - */ -exports.fn = () => { - return { - element: { - enter: (node) => { - // move group transform attr to content's pathElems - if ( - node.name === 'g' && - node.children.length !== 0 && - node.attributes.transform != null && - Object.entries(node.attributes).some( - ([name, value]) => - referencesProps.has(name) && includesUrlReference(value), - ) === false && - node.children.every( - (child) => - child.type === 'element' && - pathElemsWithGroupsAndText.includes(child.name) && - child.attributes.id == null, - ) - ) { - for (const child of node.children) { - const value = node.attributes.transform; - if (child.type === 'element') { - if (child.attributes.transform != null) { - child.attributes.transform = `${value} ${child.attributes.transform}`; - } else { - child.attributes.transform = value; - } - } - } - - delete node.attributes.transform; - } - }, - }, - }; -}; diff --git a/node_modules/svgo/plugins/plugins-types.d.ts b/node_modules/svgo/plugins/plugins-types.d.ts deleted file mode 100644 index e94e7137..00000000 --- a/node_modules/svgo/plugins/plugins-types.d.ts +++ /dev/null @@ -1,291 +0,0 @@ -import type { - Plugin as PluginDef, - PluginInfo, - XastElement, -} from '../lib/types'; - -type DefaultPlugins = { - cleanupAttrs: { - newlines?: boolean; - trim?: boolean; - spaces?: boolean; - }; - cleanupEnableBackground: void; - cleanupIds: { - remove?: boolean; - minify?: boolean; - preserve?: string[]; - preservePrefixes?: string[]; - force?: boolean; - }; - cleanupNumericValues: { - floatPrecision?: number; - leadingZero?: boolean; - defaultPx?: boolean; - convertToPx?: boolean; - }; - collapseGroups: void; - convertColors: { - currentColor?: boolean | string | RegExp; - names2hex?: boolean; - rgb2hex?: boolean; - shorthex?: boolean; - shortname?: boolean; - }; - convertEllipseToCircle: void; - convertPathData: { - applyTransforms?: boolean; - applyTransformsStroked?: boolean; - makeArcs?: { - threshold: number; - tolerance: number; - }; - straightCurves?: boolean; - convertToQ?: boolean; - lineShorthands?: boolean; - convertToZ?: boolean; - curveSmoothShorthands?: boolean; - floatPrecision?: number | false; - transformPrecision?: number; - smartArcRounding?: boolean; - removeUseless?: boolean; - collapseRepeated?: boolean; - utilizeAbsolute?: boolean; - leadingZero?: boolean; - negativeExtraSpace?: boolean; - noSpaceAfterFlags?: boolean; - forceAbsolutePath?: boolean; - }; - convertShapeToPath: { - convertArcs?: boolean; - floatPrecision?: number; - }; - convertTransform: { - convertToShorts?: boolean; - degPrecision?: number; - floatPrecision?: number; - transformPrecision?: number; - matrixToTransform?: boolean; - shortTranslate?: boolean; - shortScale?: boolean; - shortRotate?: boolean; - removeUseless?: boolean; - collapseIntoOne?: boolean; - leadingZero?: boolean; - negativeExtraSpace?: boolean; - }; - mergeStyles: void; - inlineStyles: { - /** - * Inlines selectors that match once only. - * - * @default true - */ - onlyMatchedOnce?: boolean; - /** - * Clean up matched selectors. Unused selects are left as-is. - * - * @default true - */ - removeMatchedSelectors?: boolean; - /** - * Media queries to use. An empty string indicates all selectors outside of - * media queries. - */ - useMqs?: string[]; - /** - * Pseudo-classes and elements to use. An empty string indicates all - * all non-pseudo-classes and elements. - */ - usePseudos?: string[]; - }; - mergePaths: { - force?: boolean; - floatPrecision?: number; - noSpaceAfterFlags?: boolean; - }; - - minifyStyles: { - /** - * Disable or enable a structure optimisations. - * @default true - */ - restructure?: boolean; - /** - * Enables merging of @media rules with the same media query split by other rules. - * The optimisation is unsafe in general, but should work fine in most cases. Use it on your own risk. - * @default false - */ - forceMediaMerge?: boolean; - /** - * Specify what comments to leave: - * - 'exclamation' or true – leave all exclamation comments - * - 'first-exclamation' – remove every comment except first one - * - false – remove all comments - * @default true - */ - comments?: string | boolean; - /** - * Advanced optimizations - */ - usage?: - | boolean - | { - force?: boolean; - ids?: boolean; - classes?: boolean; - tags?: boolean; - }; - }; - - moveElemsAttrsToGroup: void; - moveGroupAttrsToElems: void; - removeComments: { - preservePatterns: Array<RegExp | string> | false; - }; - removeDesc: { - removeAny?: boolean; - }; - removeDoctype: void; - removeEditorsNSData: { - additionalNamespaces?: string[]; - }; - removeEmptyAttrs: void; - removeEmptyContainers: void; - removeEmptyText: { - text?: boolean; - tspan?: boolean; - tref?: boolean; - }; - removeHiddenElems: { - isHidden?: boolean; - displayNone?: boolean; - opacity0?: boolean; - circleR0?: boolean; - ellipseRX0?: boolean; - ellipseRY0?: boolean; - rectWidth0?: boolean; - rectHeight0?: boolean; - patternWidth0?: boolean; - patternHeight0?: boolean; - imageWidth0?: boolean; - imageHeight0?: boolean; - pathEmptyD?: boolean; - polylineEmptyPoints?: boolean; - polygonEmptyPoints?: boolean; - }; - removeMetadata: void; - removeNonInheritableGroupAttrs: void; - removeTitle: void; - removeUnknownsAndDefaults: { - unknownContent?: boolean; - unknownAttrs?: boolean; - defaultAttrs?: boolean; - /** - * If to remove XML declarations that are assigned their default value. XML - * declarations are the properties in the `<?xml … ?>` block at the top of - * the document. - */ - defaultMarkupDeclarations?: boolean; - uselessOverrides?: boolean; - keepDataAttrs?: boolean; - keepAriaAttrs?: boolean; - keepRoleAttr?: boolean; - }; - removeUnusedNS: void; - removeUselessDefs: void; - removeUselessStrokeAndFill: { - stroke?: boolean; - fill?: boolean; - removeNone?: boolean; - }; - removeViewBox: void; - removeXMLProcInst: void; - sortAttrs: { - order?: string[]; - xmlnsOrder?: 'front' | 'alphabetical'; - }; - sortDefsChildren: void; -}; - -type PresetDefaultOverrides = { - [Name in keyof DefaultPlugins]?: DefaultPlugins[Name] | false; -}; - -export type BuiltinsWithOptionalParams = DefaultPlugins & { - 'preset-default': { - floatPrecision?: number; - /** - * All default plugins can be customized or disabled here - * for example - * { - * sortAttrs: { xmlnsOrder: "alphabetical" }, - * cleanupAttrs: false, - * } - */ - overrides?: PresetDefaultOverrides; - }; - cleanupListOfValues: { - floatPrecision?: number; - leadingZero?: boolean; - defaultPx?: boolean; - convertToPx?: boolean; - }; - convertOneStopGradients: void; - convertStyleToAttrs: { - keepImportant?: boolean; - }; - prefixIds: { - prefix?: - | boolean - | string - | ((node: XastElement, info: PluginInfo) => string); - delim?: string; - prefixIds?: boolean; - prefixClassNames?: boolean; - }; - removeDimensions: void; - removeOffCanvasPaths: void; - removeRasterImages: void; - removeScriptElement: void; - removeStyleElement: void; - removeXlink: { - /** - * By default this plugin ignores legacy elements that were deprecated or - * removed in SVG 2. Set to true to force performing operations on those - * too. - * - * @default false - */ - includeLegacy: boolean; - }; - removeXMLNS: void; - reusePaths: void; -}; - -export type BuiltinsWithRequiredParams = { - addAttributesToSVGElement: { - attribute?: string | Record<string, null | string>; - attributes?: Array<string | Record<string, null | string>>; - }; - addClassesToSVGElement: { - className?: string; - classNames?: string[]; - }; - removeAttributesBySelector: any; - removeAttrs: { - elemSeparator?: string; - preserveCurrentColor?: boolean; - attrs: string | string[]; - }; - removeElementsByAttr: { - id?: string | string[]; - class?: string | string[]; - }; -}; - -type PluginsParams = BuiltinsWithOptionalParams & BuiltinsWithRequiredParams; - -export type Plugin<Name extends keyof PluginsParams> = PluginDef< - PluginsParams[Name] ->; diff --git a/node_modules/svgo/plugins/prefixIds.js b/node_modules/svgo/plugins/prefixIds.js deleted file mode 100644 index ec305c6b..00000000 --- a/node_modules/svgo/plugins/prefixIds.js +++ /dev/null @@ -1,268 +0,0 @@ -'use strict'; - -/** - * @typedef {import('../lib/types.js').PluginInfo} PluginInfo - * @typedef {import('../lib/types').XastElement} XastElement - */ - -const csstree = require('css-tree'); -const { referencesProps } = require('./_collections.js'); - -exports.name = 'prefixIds'; -exports.description = 'prefix IDs'; - -/** - * extract basename from path - * @type {(path: string) => string} - */ -const getBasename = (path) => { - // extract everything after latest slash or backslash - const matched = /[/\\]?([^/\\]+)$/.exec(path); - if (matched) { - return matched[1]; - } - return ''; -}; - -/** - * escapes a string for being used as ID - * @type {(string: string) => string} - */ -const escapeIdentifierName = (str) => { - return str.replace(/[. ]/g, '_'); -}; - -/** - * @type {(string: string) => string} - */ -const unquote = (string) => { - if ( - (string.startsWith('"') && string.endsWith('"')) || - (string.startsWith("'") && string.endsWith("'")) - ) { - return string.slice(1, -1); - } - return string; -}; - -/** - * Prefix the given string, unless it already starts with the generated prefix. - * - * @param {(id: string) => string} prefixGenerator Function to generate a prefix. - * @param {string} body An arbitrary string. - * @returns {string} The given string with a prefix prepended to it. - */ -const prefixId = (prefixGenerator, body) => { - const prefix = prefixGenerator(body); - if (body.startsWith(prefix)) { - return body; - } - return prefix + body; -}; - -/** - * Insert the prefix in a reference string. A reference string is already - * prefixed with #, so the prefix is inserted after the first character. - * - * @param {(id: string) => string} prefixGenerator Function to generate a prefix. - * @param {string} reference An arbitrary string, should start with "#". - * @returns {?string} The given string with a prefix inserted, or null if the string did not start with "#". - */ -const prefixReference = (prefixGenerator, reference) => { - if (reference.startsWith('#')) { - return '#' + prefixId(prefixGenerator, reference.slice(1)); - } - return null; -}; - -/** - * Generates a prefix for the given string. - * - * @param {string} body An arbitrary string. - * @param {XastElement} node XML node that the identifier belongs to. - * @param {PluginInfo} info - * @param {((node: XastElement, info: PluginInfo) => string)|string|boolean|undefined} prefixGenerator Some way of obtaining a prefix. - * @param {string} delim Content to insert between the prefix and original value. - * @param {Map<string, string>} history Map of previously generated prefixes to IDs. - * @returns {string} A generated prefix. - */ -const generatePrefix = (body, node, info, prefixGenerator, delim, history) => { - if (typeof prefixGenerator === 'function') { - let prefix = history.get(body); - - if (prefix != null) { - return prefix; - } - - prefix = prefixGenerator(node, info) + delim; - history.set(body, prefix); - return prefix; - } - - if (typeof prefixGenerator === 'string') { - return prefixGenerator + delim; - } - - if (prefixGenerator === false) { - return ''; - } - - if (info.path != null && info.path.length > 0) { - return escapeIdentifierName(getBasename(info.path)) + delim; - } - - return 'prefix' + delim; -}; - -/** - * Prefixes identifiers - * - * @author strarsis <strarsis@gmail.com> - * @type {import('./plugins-types').Plugin<'prefixIds'>} - */ -exports.fn = (_root, params, info) => { - const { - delim = '__', - prefix, - prefixIds = true, - prefixClassNames = true, - } = params; - - /** @type {Map<string, string>} */ - const prefixMap = new Map(); - - return { - element: { - enter: (node) => { - /** - * @param {string} id A node identifier or class. - * @returns {string} Given string with a prefix inserted, or null if the string did not start with "#". - */ - const prefixGenerator = (id) => - generatePrefix(id, node, info, prefix, delim, prefixMap); - - // prefix id/class selectors and url() references in styles - if (node.name === 'style') { - // skip empty <style/> elements - if (node.children.length === 0) { - return; - } - - for (const child of node.children) { - if (child.type !== 'text' && child.type !== 'cdata') { - continue; - } - - const cssText = child.value; - /** @type {?csstree.CssNode} */ - let cssAst = null; - try { - cssAst = csstree.parse(cssText, { - parseValue: true, - parseCustomProperty: false, - }); - } catch { - return; - } - - csstree.walk(cssAst, (node) => { - if ( - (prefixIds && node.type === 'IdSelector') || - (prefixClassNames && node.type === 'ClassSelector') - ) { - node.name = prefixId(prefixGenerator, node.name); - return; - } - if (node.type === 'Url' && node.value.length > 0) { - const prefixed = prefixReference( - prefixGenerator, - unquote(node.value), - ); - if (prefixed != null) { - node.value = prefixed; - } - } - }); - - child.value = csstree.generate(cssAst); - return; - } - } - - // prefix an ID attribute value - if ( - prefixIds && - node.attributes.id != null && - node.attributes.id.length !== 0 - ) { - node.attributes.id = prefixId(prefixGenerator, node.attributes.id); - } - - // prefix a class attribute value - if ( - prefixClassNames && - node.attributes.class != null && - node.attributes.class.length !== 0 - ) { - node.attributes.class = node.attributes.class - .split(/\s+/) - .map((name) => prefixId(prefixGenerator, name)) - .join(' '); - } - - // prefix a href attribute value - // xlink:href is deprecated, must be still supported - for (const name of ['href', 'xlink:href']) { - if ( - node.attributes[name] != null && - node.attributes[name].length !== 0 - ) { - const prefixed = prefixReference( - prefixGenerator, - node.attributes[name], - ); - if (prefixed != null) { - node.attributes[name] = prefixed; - } - } - } - - // prefix a URL attribute value - for (const name of referencesProps) { - if ( - node.attributes[name] != null && - node.attributes[name].length !== 0 - ) { - node.attributes[name] = node.attributes[name].replace( - /\burl\((["'])?(#.+?)\1\)/gi, - (match, _, url) => { - const prefixed = prefixReference(prefixGenerator, url); - if (prefixed == null) { - return match; - } - return `url(${prefixed})`; - }, - ); - } - } - - // prefix begin/end attribute value - for (const name of ['begin', 'end']) { - if ( - node.attributes[name] != null && - node.attributes[name].length !== 0 - ) { - const parts = node.attributes[name].split(/\s*;\s+/).map((val) => { - if (val.endsWith('.end') || val.endsWith('.start')) { - const [id, postfix] = val.split('.'); - return `${prefixId(prefixGenerator, id)}.${postfix}`; - } - return val; - }); - node.attributes[name] = parts.join('; '); - } - } - }, - }, - }; -}; diff --git a/node_modules/svgo/plugins/preset-default.js b/node_modules/svgo/plugins/preset-default.js deleted file mode 100644 index 86516fd7..00000000 --- a/node_modules/svgo/plugins/preset-default.js +++ /dev/null @@ -1,82 +0,0 @@ -'use strict'; - -const { createPreset } = require('../lib/svgo/plugins.js'); - -const removeDoctype = require('./removeDoctype.js'); -const removeXMLProcInst = require('./removeXMLProcInst.js'); -const removeComments = require('./removeComments.js'); -const removeMetadata = require('./removeMetadata.js'); -const removeEditorsNSData = require('./removeEditorsNSData.js'); -const cleanupAttrs = require('./cleanupAttrs.js'); -const mergeStyles = require('./mergeStyles.js'); -const inlineStyles = require('./inlineStyles.js'); -const minifyStyles = require('./minifyStyles.js'); -const cleanupIds = require('./cleanupIds.js'); -const removeUselessDefs = require('./removeUselessDefs.js'); -const cleanupNumericValues = require('./cleanupNumericValues.js'); -const convertColors = require('./convertColors.js'); -const removeUnknownsAndDefaults = require('./removeUnknownsAndDefaults.js'); -const removeNonInheritableGroupAttrs = require('./removeNonInheritableGroupAttrs.js'); -const removeUselessStrokeAndFill = require('./removeUselessStrokeAndFill.js'); -const removeViewBox = require('./removeViewBox.js'); -const cleanupEnableBackground = require('./cleanupEnableBackground.js'); -const removeHiddenElems = require('./removeHiddenElems.js'); -const removeEmptyText = require('./removeEmptyText.js'); -const convertShapeToPath = require('./convertShapeToPath.js'); -const convertEllipseToCircle = require('./convertEllipseToCircle.js'); -const moveElemsAttrsToGroup = require('./moveElemsAttrsToGroup.js'); -const moveGroupAttrsToElems = require('./moveGroupAttrsToElems.js'); -const collapseGroups = require('./collapseGroups.js'); -const convertPathData = require('./convertPathData.js'); -const convertTransform = require('./convertTransform.js'); -const removeEmptyAttrs = require('./removeEmptyAttrs.js'); -const removeEmptyContainers = require('./removeEmptyContainers.js'); -const mergePaths = require('./mergePaths.js'); -const removeUnusedNS = require('./removeUnusedNS.js'); -const sortAttrs = require('./sortAttrs.js'); -const sortDefsChildren = require('./sortDefsChildren.js'); -const removeTitle = require('./removeTitle.js'); -const removeDesc = require('./removeDesc.js'); - -const presetDefault = createPreset({ - name: 'preset-default', - plugins: [ - removeDoctype, - removeXMLProcInst, - removeComments, - removeMetadata, - removeEditorsNSData, - cleanupAttrs, - mergeStyles, - inlineStyles, - minifyStyles, - cleanupIds, - removeUselessDefs, - cleanupNumericValues, - convertColors, - removeUnknownsAndDefaults, - removeNonInheritableGroupAttrs, - removeUselessStrokeAndFill, - removeViewBox, - cleanupEnableBackground, - removeHiddenElems, - removeEmptyText, - convertShapeToPath, - convertEllipseToCircle, - moveElemsAttrsToGroup, - moveGroupAttrsToElems, - collapseGroups, - convertPathData, - convertTransform, - removeEmptyAttrs, - removeEmptyContainers, - mergePaths, - removeUnusedNS, - sortAttrs, - sortDefsChildren, - removeTitle, - removeDesc, - ], -}); - -module.exports = presetDefault; diff --git a/node_modules/svgo/plugins/removeAttributesBySelector.js b/node_modules/svgo/plugins/removeAttributesBySelector.js deleted file mode 100644 index 12630c2a..00000000 --- a/node_modules/svgo/plugins/removeAttributesBySelector.js +++ /dev/null @@ -1,97 +0,0 @@ -'use strict'; - -const { querySelectorAll } = require('../lib/xast.js'); - -exports.name = 'removeAttributesBySelector'; -exports.description = - 'removes attributes of elements that match a css selector'; - -/** - * Removes attributes of elements that match a css selector. - * - * @example - * <caption>A selector removing a single attribute</caption> - * plugins: [ - * { - * name: "removeAttributesBySelector", - * params: { - * selector: "[fill='#00ff00']" - * attributes: "fill" - * } - * } - * ] - * - * <rect x="0" y="0" width="100" height="100" fill="#00ff00" stroke="#00ff00"/> - * ↓ - * <rect x="0" y="0" width="100" height="100" stroke="#00ff00"/> - * - * <caption>A selector removing multiple attributes</caption> - * plugins: [ - * { - * name: "removeAttributesBySelector", - * params: { - * selector: "[fill='#00ff00']", - * attributes: [ - * "fill", - * "stroke" - * ] - * } - * } - * ] - * - * <rect x="0" y="0" width="100" height="100" fill="#00ff00" stroke="#00ff00"/> - * ↓ - * <rect x="0" y="0" width="100" height="100"/> - * - * <caption>Multiple selectors removing attributes</caption> - * plugins: [ - * { - * name: "removeAttributesBySelector", - * params: { - * selectors: [ - * { - * selector: "[fill='#00ff00']", - * attributes: "fill" - * }, - * { - * selector: "#remove", - * attributes: [ - * "stroke", - * "id" - * ] - * } - * ] - * } - * } - * ] - * - * <rect x="0" y="0" width="100" height="100" fill="#00ff00" stroke="#00ff00"/> - * ↓ - * <rect x="0" y="0" width="100" height="100"/> - * - * @link https://developer.mozilla.org/docs/Web/CSS/CSS_Selectors|MDN CSS Selectors - * - * @author Bradley Mease - * - * @type {import('./plugins-types').Plugin<'removeAttributesBySelector'>} - */ -exports.fn = (root, params) => { - const selectors = Array.isArray(params.selectors) - ? params.selectors - : [params]; - for (const { selector, attributes } of selectors) { - const nodes = querySelectorAll(root, selector); - for (const node of nodes) { - if (node.type === 'element') { - if (Array.isArray(attributes)) { - for (const name of attributes) { - delete node.attributes[name]; - } - } else { - delete node.attributes[attributes]; - } - } - } - } - return {}; -}; diff --git a/node_modules/svgo/plugins/removeAttrs.js b/node_modules/svgo/plugins/removeAttrs.js deleted file mode 100644 index dc1224bd..00000000 --- a/node_modules/svgo/plugins/removeAttrs.js +++ /dev/null @@ -1,151 +0,0 @@ -'use strict'; - -exports.name = 'removeAttrs'; -exports.description = 'removes specified attributes'; - -const DEFAULT_SEPARATOR = ':'; -const ENOATTRS = `Warning: The plugin "removeAttrs" requires the "attrs" parameter. -It should have a pattern to remove, otherwise the plugin is a noop. -Config example: - -plugins: [ - { - name: "removeAttrs", - params: { - attrs: "(fill|stroke)" - } - } -] -`; - -/** - * Remove attributes - * - * @example elemSeparator - * format: string - * - * @example preserveCurrentColor - * format: boolean - * - * @example attrs: - * - * format: [ element* : attribute* : value* ] - * - * element : regexp (wrapped into ^...$), single * or omitted > all elements (must be present when value is used) - * attribute : regexp (wrapped into ^...$) - * value : regexp (wrapped into ^...$), single * or omitted > all values - * - * examples: - * - * > basic: remove fill attribute - * --- - * removeAttrs: - * attrs: 'fill' - * - * > remove fill attribute on path element - * --- - * attrs: 'path:fill' - * - * > remove fill attribute on path element where value is none - * --- - * attrs: 'path:fill:none' - * - * - * > remove all fill and stroke attribute - * --- - * attrs: - * - 'fill' - * - 'stroke' - * - * [is same as] - * - * attrs: '(fill|stroke)' - * - * [is same as] - * - * attrs: '*:(fill|stroke)' - * - * [is same as] - * - * attrs: '.*:(fill|stroke)' - * - * [is same as] - * - * attrs: '.*:(fill|stroke):.*' - * - * - * > remove all stroke related attributes - * ---- - * attrs: 'stroke.*' - * - * - * @author Benny Schudel - * - * @type {import('./plugins-types').Plugin<'removeAttrs'>} - */ -exports.fn = (root, params) => { - if (typeof params.attrs == 'undefined') { - console.warn(ENOATTRS); - return null; - } - - const elemSeparator = - typeof params.elemSeparator == 'string' - ? params.elemSeparator - : DEFAULT_SEPARATOR; - const preserveCurrentColor = - typeof params.preserveCurrentColor == 'boolean' - ? params.preserveCurrentColor - : false; - const attrs = Array.isArray(params.attrs) ? params.attrs : [params.attrs]; - - return { - element: { - enter: (node) => { - for (let pattern of attrs) { - // if no element separators (:), assume it's attribute name, and apply to all elements *regardless of value* - if (!pattern.includes(elemSeparator)) { - pattern = ['.*', pattern, '.*'].join(elemSeparator); - // if only 1 separator, assume it's element and attribute name, and apply regardless of attribute value - } else if (pattern.split(elemSeparator).length < 3) { - pattern = [pattern, '.*'].join(elemSeparator); - } - - // create regexps for element, attribute name, and attribute value - const list = pattern.split(elemSeparator).map((value) => { - // adjust single * to match anything - if (value === '*') { - value = '.*'; - } - return new RegExp(['^', value, '$'].join(''), 'i'); - }); - - // matches element - if (list[0].test(node.name)) { - // loop attributes - for (const [name, value] of Object.entries(node.attributes)) { - const isFillCurrentColor = - preserveCurrentColor && - name == 'fill' && - value == 'currentColor'; - const isStrokeCurrentColor = - preserveCurrentColor && - name == 'stroke' && - value == 'currentColor'; - if ( - !isFillCurrentColor && - !isStrokeCurrentColor && - // matches attribute name - list[1].test(name) && - // matches attribute value - list[2].test(value) - ) { - delete node.attributes[name]; - } - } - } - } - }, - }, - }; -}; diff --git a/node_modules/svgo/plugins/removeComments.js b/node_modules/svgo/plugins/removeComments.js deleted file mode 100644 index 46a32590..00000000 --- a/node_modules/svgo/plugins/removeComments.js +++ /dev/null @@ -1,51 +0,0 @@ -'use strict'; - -const { detachNodeFromParent } = require('../lib/xast.js'); - -exports.name = 'removeComments'; -exports.description = 'removes comments'; - -/** - * If a comment matches one of the following patterns, it will be - * preserved by default. Particularly for copyright/license information. - */ -const DEFAULT_PRESERVE_PATTERNS = [/^!/]; - -/** - * Remove comments. - * - * @example - * <!-- Generator: Adobe Illustrator 15.0.0, SVG Export - * Plug-In . SVG Version: 6.00 Build 0) --> - * - * @author Kir Belevich - * - * @type {import('./plugins-types').Plugin<'removeComments'>} - */ -exports.fn = (_root, params) => { - const { preservePatterns = DEFAULT_PRESERVE_PATTERNS } = params; - - return { - comment: { - enter: (node, parentNode) => { - if (preservePatterns) { - if (!Array.isArray(preservePatterns)) { - throw Error( - `Expected array in removeComments preservePatterns parameter but received ${preservePatterns}`, - ); - } - - const matches = preservePatterns.some((pattern) => { - return new RegExp(pattern).test(node.value); - }); - - if (matches) { - return; - } - } - - detachNodeFromParent(node, parentNode); - }, - }, - }; -}; diff --git a/node_modules/svgo/plugins/removeDesc.js b/node_modules/svgo/plugins/removeDesc.js deleted file mode 100644 index f766350e..00000000 --- a/node_modules/svgo/plugins/removeDesc.js +++ /dev/null @@ -1,39 +0,0 @@ -'use strict'; - -const { detachNodeFromParent } = require('../lib/xast.js'); - -exports.name = 'removeDesc'; -exports.description = 'removes <desc>'; - -const standardDescs = /^(Created with|Created using)/; - -/** - * Removes <desc>. - * Removes only standard editors content or empty elements 'cause it can be used for accessibility. - * Enable parameter 'removeAny' to remove any description. - * - * https://developer.mozilla.org/docs/Web/SVG/Element/desc - * - * @author Daniel Wabyick - * - * @type {import('./plugins-types').Plugin<'removeDesc'>} - */ -exports.fn = (root, params) => { - const { removeAny = false } = params; - return { - element: { - enter: (node, parentNode) => { - if (node.name === 'desc') { - if ( - removeAny || - node.children.length === 0 || - (node.children[0].type === 'text' && - standardDescs.test(node.children[0].value)) - ) { - detachNodeFromParent(node, parentNode); - } - } - }, - }, - }; -}; diff --git a/node_modules/svgo/plugins/removeDimensions.js b/node_modules/svgo/plugins/removeDimensions.js deleted file mode 100644 index 4f4f3984..00000000 --- a/node_modules/svgo/plugins/removeDimensions.js +++ /dev/null @@ -1,43 +0,0 @@ -'use strict'; - -exports.name = 'removeDimensions'; -exports.description = - 'removes width and height in presence of viewBox (opposite to removeViewBox, disable it first)'; - -/** - * Remove width/height attributes and add the viewBox attribute if it's missing - * - * @example - * <svg width="100" height="50" /> - * ↓ - * <svg viewBox="0 0 100 50" /> - * - * @author Benny Schudel - * - * @type {import('./plugins-types').Plugin<'removeDimensions'>} - */ -exports.fn = () => { - return { - element: { - enter: (node) => { - if (node.name === 'svg') { - if (node.attributes.viewBox != null) { - delete node.attributes.width; - delete node.attributes.height; - } else if ( - node.attributes.width != null && - node.attributes.height != null && - Number.isNaN(Number(node.attributes.width)) === false && - Number.isNaN(Number(node.attributes.height)) === false - ) { - const width = Number(node.attributes.width); - const height = Number(node.attributes.height); - node.attributes.viewBox = `0 0 ${width} ${height}`; - delete node.attributes.width; - delete node.attributes.height; - } - } - }, - }, - }; -}; diff --git a/node_modules/svgo/plugins/removeDoctype.js b/node_modules/svgo/plugins/removeDoctype.js deleted file mode 100644 index cbdcd013..00000000 --- a/node_modules/svgo/plugins/removeDoctype.js +++ /dev/null @@ -1,40 +0,0 @@ -'use strict'; - -const { detachNodeFromParent } = require('../lib/xast.js'); - -exports.name = 'removeDoctype'; -exports.description = 'removes doctype declaration'; - -/** - * Remove DOCTYPE declaration. - * - * "Unfortunately the SVG DTDs are a source of so many - * issues that the SVG WG has decided not to write one - * for the upcoming SVG 1.2 standard. In fact SVG WG - * members are even telling people not to use a DOCTYPE - * declaration in SVG 1.0 and 1.1 documents" - * https://jwatt.org/svg/authoring/#doctype-declaration - * - * @example - * <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" - * q"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> - * - * @example - * <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" - * "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd" [ - * <!-- an internal subset can be embedded here --> - * ]> - * - * @author Kir Belevich - * - * @type {import('./plugins-types').Plugin<'removeDoctype'>} - */ -exports.fn = () => { - return { - doctype: { - enter: (node, parentNode) => { - detachNodeFromParent(node, parentNode); - }, - }, - }; -}; diff --git a/node_modules/svgo/plugins/removeEditorsNSData.js b/node_modules/svgo/plugins/removeEditorsNSData.js deleted file mode 100644 index 852681bf..00000000 --- a/node_modules/svgo/plugins/removeEditorsNSData.js +++ /dev/null @@ -1,64 +0,0 @@ -'use strict'; - -const { detachNodeFromParent } = require('../lib/xast.js'); -const { editorNamespaces } = require('./_collections.js'); - -exports.name = 'removeEditorsNSData'; -exports.description = 'removes editors namespaces, elements and attributes'; - -/** - * Remove editors namespaces, elements and attributes. - * - * @example - * <svg xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"> - * <sodipodi:namedview/> - * <path sodipodi:nodetypes="cccc"/> - * - * @author Kir Belevich - * - * @type {import('./plugins-types').Plugin<'removeEditorsNSData'>} - */ -exports.fn = (_root, params) => { - let namespaces = [...editorNamespaces]; - if (Array.isArray(params.additionalNamespaces)) { - namespaces = [...editorNamespaces, ...params.additionalNamespaces]; - } - /** - * @type {string[]} - */ - const prefixes = []; - return { - element: { - enter: (node, parentNode) => { - // collect namespace prefixes from svg element - if (node.name === 'svg') { - for (const [name, value] of Object.entries(node.attributes)) { - if (name.startsWith('xmlns:') && namespaces.includes(value)) { - prefixes.push(name.slice('xmlns:'.length)); - // <svg xmlns:sodipodi=""> - delete node.attributes[name]; - } - } - } - // remove editor attributes, for example - // <* sodipodi:*=""> - for (const name of Object.keys(node.attributes)) { - if (name.includes(':')) { - const [prefix] = name.split(':'); - if (prefixes.includes(prefix)) { - delete node.attributes[name]; - } - } - } - // remove editor elements, for example - // <sodipodi:*> - if (node.name.includes(':')) { - const [prefix] = node.name.split(':'); - if (prefixes.includes(prefix)) { - detachNodeFromParent(node, parentNode); - } - } - }, - }, - }; -}; diff --git a/node_modules/svgo/plugins/removeElementsByAttr.js b/node_modules/svgo/plugins/removeElementsByAttr.js deleted file mode 100644 index 9c9915f0..00000000 --- a/node_modules/svgo/plugins/removeElementsByAttr.js +++ /dev/null @@ -1,73 +0,0 @@ -'use strict'; - -const { detachNodeFromParent } = require('../lib/xast.js'); - -exports.name = 'removeElementsByAttr'; -exports.description = - 'removes arbitrary elements by ID or className (disabled by default)'; - -/** - * Remove arbitrary SVG elements by ID or className. - * - * @example id - * > single: remove element with ID of `elementID` - * --- - * removeElementsByAttr: - * id: 'elementID' - * - * > list: remove multiple elements by ID - * --- - * removeElementsByAttr: - * id: - * - 'elementID' - * - 'anotherID' - * - * @example class - * > single: remove all elements with class of `elementClass` - * --- - * removeElementsByAttr: - * class: 'elementClass' - * - * > list: remove all elements with class of `elementClass` or `anotherClass` - * --- - * removeElementsByAttr: - * class: - * - 'elementClass' - * - 'anotherClass' - * - * @author Eli Dupuis (@elidupuis) - * - * @type {import('./plugins-types').Plugin<'removeElementsByAttr'>} - */ -exports.fn = (root, params) => { - const ids = - params.id == null ? [] : Array.isArray(params.id) ? params.id : [params.id]; - const classes = - params.class == null - ? [] - : Array.isArray(params.class) - ? params.class - : [params.class]; - return { - element: { - enter: (node, parentNode) => { - // remove element if it's `id` matches configured `id` params - if (node.attributes.id != null && ids.length !== 0) { - if (ids.includes(node.attributes.id)) { - detachNodeFromParent(node, parentNode); - } - } - // remove element if it's `class` contains any of the configured `class` params - if (node.attributes.class && classes.length !== 0) { - const classList = node.attributes.class.split(' '); - for (const item of classes) { - if (classList.includes(item)) { - detachNodeFromParent(node, parentNode); - break; - } - } - } - }, - }, - }; -}; diff --git a/node_modules/svgo/plugins/removeEmptyAttrs.js b/node_modules/svgo/plugins/removeEmptyAttrs.js deleted file mode 100644 index 194d27b8..00000000 --- a/node_modules/svgo/plugins/removeEmptyAttrs.js +++ /dev/null @@ -1,31 +0,0 @@ -'use strict'; - -const { attrsGroups } = require('./_collections.js'); - -exports.name = 'removeEmptyAttrs'; -exports.description = 'removes empty attributes'; - -/** - * Remove attributes with empty values. - * - * @author Kir Belevich - * - * @type {import('./plugins-types').Plugin<'removeEmptyAttrs'>} - */ -exports.fn = () => { - return { - element: { - enter: (node) => { - for (const [name, value] of Object.entries(node.attributes)) { - if ( - value === '' && - // empty conditional processing attributes prevents elements from rendering - !attrsGroups.conditionalProcessing.has(name) - ) { - delete node.attributes[name]; - } - } - }, - }, - }; -}; diff --git a/node_modules/svgo/plugins/removeEmptyContainers.js b/node_modules/svgo/plugins/removeEmptyContainers.js deleted file mode 100644 index 03907344..00000000 --- a/node_modules/svgo/plugins/removeEmptyContainers.js +++ /dev/null @@ -1,59 +0,0 @@ -'use strict'; - -const { detachNodeFromParent } = require('../lib/xast.js'); -const { elemsGroups } = require('./_collections.js'); - -exports.name = 'removeEmptyContainers'; -exports.description = 'removes empty container elements'; - -/** - * Remove empty containers. - * - * @see https://www.w3.org/TR/SVG11/intro.html#TermContainerElement - * - * @example - * <defs/> - * - * @example - * <g><marker><a/></marker></g> - * - * @author Kir Belevich - * - * @type {import('./plugins-types').Plugin<'removeEmptyContainers'>} - */ -exports.fn = () => { - return { - element: { - exit: (node, parentNode) => { - // remove only empty non-svg containers - if ( - node.name === 'svg' || - !elemsGroups.container.has(node.name) || - node.children.length !== 0 - ) { - return; - } - // empty patterns may contain reusable configuration - if ( - node.name === 'pattern' && - Object.keys(node.attributes).length !== 0 - ) { - return; - } - // The <g> may not have content, but the filter may cause a rectangle - // to be created and filled with pattern. - if (node.name === 'g' && node.attributes.filter != null) { - return; - } - // empty <mask> hides masked element - if (node.name === 'mask' && node.attributes.id != null) { - return; - } - if (parentNode.type === 'element' && parentNode.name === 'switch') { - return; - } - detachNodeFromParent(node, parentNode); - }, - }, - }; -}; diff --git a/node_modules/svgo/plugins/removeEmptyText.js b/node_modules/svgo/plugins/removeEmptyText.js deleted file mode 100644 index 5f6db3d3..00000000 --- a/node_modules/svgo/plugins/removeEmptyText.js +++ /dev/null @@ -1,51 +0,0 @@ -'use strict'; - -const { detachNodeFromParent } = require('../lib/xast.js'); - -exports.name = 'removeEmptyText'; -exports.description = 'removes empty <text> elements'; - -/** - * Remove empty Text elements. - * - * @see https://www.w3.org/TR/SVG11/text.html - * - * @example - * Remove empty text element: - * <text/> - * - * Remove empty tspan element: - * <tspan/> - * - * Remove tref with empty xlink:href attribute: - * <tref xlink:href=""/> - * - * @author Kir Belevich - * - * @type {import('./plugins-types').Plugin<'removeEmptyText'>} - */ -exports.fn = (root, params) => { - const { text = true, tspan = true, tref = true } = params; - return { - element: { - enter: (node, parentNode) => { - // Remove empty text element - if (text && node.name === 'text' && node.children.length === 0) { - detachNodeFromParent(node, parentNode); - } - // Remove empty tspan element - if (tspan && node.name === 'tspan' && node.children.length === 0) { - detachNodeFromParent(node, parentNode); - } - // Remove tref with empty xlink:href attribute - if ( - tref && - node.name === 'tref' && - node.attributes['xlink:href'] == null - ) { - detachNodeFromParent(node, parentNode); - } - }, - }, - }; -}; diff --git a/node_modules/svgo/plugins/removeHiddenElems.js b/node_modules/svgo/plugins/removeHiddenElems.js deleted file mode 100644 index 75720a06..00000000 --- a/node_modules/svgo/plugins/removeHiddenElems.js +++ /dev/null @@ -1,440 +0,0 @@ -'use strict'; - -/** - * @typedef {import('../lib/types').XastChild} XastChild - * @typedef {import('../lib/types').XastElement} XastElement - * @typedef {import('../lib/types').XastParent} XastParent - */ - -const { elemsGroups } = require('./_collections.js'); -const { - visit, - visitSkip, - querySelector, - detachNodeFromParent, -} = require('../lib/xast.js'); -const { collectStylesheet, computeStyle } = require('../lib/style.js'); -const { parsePathData } = require('../lib/path.js'); -const { hasScripts, findReferences } = require('../lib/svgo/tools.js'); - -const nonRendering = elemsGroups.nonRendering; - -exports.name = 'removeHiddenElems'; -exports.description = - 'removes hidden elements (zero sized, with absent attributes)'; - -/** - * Remove hidden elements with disabled rendering: - * - display="none" - * - opacity="0" - * - circle with zero radius - * - ellipse with zero x-axis or y-axis radius - * - rectangle with zero width or height - * - pattern with zero width or height - * - image with zero width or height - * - path with empty data - * - polyline with empty points - * - polygon with empty points - * - * @author Kir Belevich - * - * @type {import('./plugins-types').Plugin<'removeHiddenElems'>} - */ -exports.fn = (root, params) => { - const { - isHidden = true, - displayNone = true, - opacity0 = true, - circleR0 = true, - ellipseRX0 = true, - ellipseRY0 = true, - rectWidth0 = true, - rectHeight0 = true, - patternWidth0 = true, - patternHeight0 = true, - imageWidth0 = true, - imageHeight0 = true, - pathEmptyD = true, - polylineEmptyPoints = true, - polygonEmptyPoints = true, - } = params; - const stylesheet = collectStylesheet(root); - - /** - * Skip non-rendered nodes initially, and only detach if they have no ID, or - * their ID is not referenced by another node. - * - * @type {Map<XastElement, XastParent>} - */ - const nonRenderedNodes = new Map(); - - /** - * IDs for removed hidden definitions. - * - * @type {Set<string>} - */ - const removedDefIds = new Set(); - - /** - * @type {Map<XastElement, XastParent>} - */ - const allDefs = new Map(); - - /** @type {Set<string>} */ - const allReferences = new Set(); - - /** - * @type {Map<string, Array<{ node: XastElement, parentNode: XastParent }>>} - */ - const referencesById = new Map(); - - /** - * If styles are present, we can't be sure if a definition is unused or not - */ - let deoptimized = false; - - /** - * @param {XastChild} node - * @param {XastParent} parentNode - */ - function removeElement(node, parentNode) { - if ( - node.type === 'element' && - node.attributes.id != null && - parentNode.type === 'element' && - parentNode.name === 'defs' - ) { - removedDefIds.add(node.attributes.id); - } - - detachNodeFromParent(node, parentNode); - } - - visit(root, { - element: { - enter: (node, parentNode) => { - // transparent non-rendering elements still apply where referenced - if (nonRendering.has(node.name)) { - if (node.attributes.id == null) { - detachNodeFromParent(node, parentNode); - return visitSkip; - } - - nonRenderedNodes.set(node, parentNode); - return visitSkip; - } - const computedStyle = computeStyle(stylesheet, node); - // opacity="0" - // - // https://www.w3.org/TR/SVG11/masking.html#ObjectAndGroupOpacityProperties - if ( - opacity0 && - computedStyle.opacity && - computedStyle.opacity.type === 'static' && - computedStyle.opacity.value === '0' - ) { - removeElement(node, parentNode); - } - }, - }, - }); - - return { - element: { - enter: (node, parentNode) => { - if ( - (node.name === 'style' && node.children.length !== 0) || - hasScripts(node) - ) { - deoptimized = true; - return; - } - - if (node.name === 'defs') { - allDefs.set(node, parentNode); - } - - if (node.name === 'use') { - for (const attr of Object.keys(node.attributes)) { - if (attr !== 'href' && !attr.endsWith(':href')) continue; - const value = node.attributes[attr]; - const id = value.slice(1); - - let refs = referencesById.get(id); - if (!refs) { - refs = []; - referencesById.set(id, refs); - } - refs.push({ node, parentNode }); - } - } - - // Removes hidden elements - // https://www.w3schools.com/cssref/pr_class_visibility.asp - const computedStyle = computeStyle(stylesheet, node); - if ( - isHidden && - computedStyle.visibility && - computedStyle.visibility.type === 'static' && - computedStyle.visibility.value === 'hidden' && - // keep if any descendant enables visibility - querySelector(node, '[visibility=visible]') == null - ) { - removeElement(node, parentNode); - return; - } - - // display="none" - // - // https://www.w3.org/TR/SVG11/painting.html#DisplayProperty - // "A value of display: none indicates that the given element - // and its children shall not be rendered directly" - if ( - displayNone && - computedStyle.display && - computedStyle.display.type === 'static' && - computedStyle.display.value === 'none' && - // markers with display: none still rendered - node.name !== 'marker' - ) { - removeElement(node, parentNode); - return; - } - - // Circles with zero radius - // - // https://www.w3.org/TR/SVG11/shapes.html#CircleElementRAttribute - // "A value of zero disables rendering of the element" - // - // <circle r="0"> - if ( - circleR0 && - node.name === 'circle' && - node.children.length === 0 && - node.attributes.r === '0' - ) { - removeElement(node, parentNode); - return; - } - - // Ellipse with zero x-axis radius - // - // https://www.w3.org/TR/SVG11/shapes.html#EllipseElementRXAttribute - // "A value of zero disables rendering of the element" - // - // <ellipse rx="0"> - if ( - ellipseRX0 && - node.name === 'ellipse' && - node.children.length === 0 && - node.attributes.rx === '0' - ) { - removeElement(node, parentNode); - return; - } - - // Ellipse with zero y-axis radius - // - // https://www.w3.org/TR/SVG11/shapes.html#EllipseElementRYAttribute - // "A value of zero disables rendering of the element" - // - // <ellipse ry="0"> - if ( - ellipseRY0 && - node.name === 'ellipse' && - node.children.length === 0 && - node.attributes.ry === '0' - ) { - removeElement(node, parentNode); - return; - } - - // Rectangle with zero width - // - // https://www.w3.org/TR/SVG11/shapes.html#RectElementWidthAttribute - // "A value of zero disables rendering of the element" - // - // <rect width="0"> - if ( - rectWidth0 && - node.name === 'rect' && - node.children.length === 0 && - node.attributes.width === '0' - ) { - removeElement(node, parentNode); - return; - } - - // Rectangle with zero height - // - // https://www.w3.org/TR/SVG11/shapes.html#RectElementHeightAttribute - // "A value of zero disables rendering of the element" - // - // <rect height="0"> - if ( - rectHeight0 && - rectWidth0 && - node.name === 'rect' && - node.children.length === 0 && - node.attributes.height === '0' - ) { - removeElement(node, parentNode); - return; - } - - // Pattern with zero width - // - // https://www.w3.org/TR/SVG11/pservers.html#PatternElementWidthAttribute - // "A value of zero disables rendering of the element (i.e., no paint is applied)" - // - // <pattern width="0"> - if ( - patternWidth0 && - node.name === 'pattern' && - node.attributes.width === '0' - ) { - removeElement(node, parentNode); - return; - } - - // Pattern with zero height - // - // https://www.w3.org/TR/SVG11/pservers.html#PatternElementHeightAttribute - // "A value of zero disables rendering of the element (i.e., no paint is applied)" - // - // <pattern height="0"> - if ( - patternHeight0 && - node.name === 'pattern' && - node.attributes.height === '0' - ) { - removeElement(node, parentNode); - return; - } - - // Image with zero width - // - // https://www.w3.org/TR/SVG11/struct.html#ImageElementWidthAttribute - // "A value of zero disables rendering of the element" - // - // <image width="0"> - if ( - imageWidth0 && - node.name === 'image' && - node.attributes.width === '0' - ) { - removeElement(node, parentNode); - return; - } - - // Image with zero height - // - // https://www.w3.org/TR/SVG11/struct.html#ImageElementHeightAttribute - // "A value of zero disables rendering of the element" - // - // <image height="0"> - if ( - imageHeight0 && - node.name === 'image' && - node.attributes.height === '0' - ) { - removeElement(node, parentNode); - return; - } - - // Path with empty data - // - // https://www.w3.org/TR/SVG11/paths.html#DAttribute - // - // <path d=""/> - if (pathEmptyD && node.name === 'path') { - if (node.attributes.d == null) { - removeElement(node, parentNode); - return; - } - const pathData = parsePathData(node.attributes.d); - if (pathData.length === 0) { - removeElement(node, parentNode); - return; - } - // keep single point paths for markers - if ( - pathData.length === 1 && - computedStyle['marker-start'] == null && - computedStyle['marker-end'] == null - ) { - removeElement(node, parentNode); - return; - } - } - - // Polyline with empty points - // - // https://www.w3.org/TR/SVG11/shapes.html#PolylineElementPointsAttribute - // - // <polyline points=""> - if ( - polylineEmptyPoints && - node.name === 'polyline' && - node.attributes.points == null - ) { - removeElement(node, parentNode); - return; - } - - // Polygon with empty points - // - // https://www.w3.org/TR/SVG11/shapes.html#PolygonElementPointsAttribute - // - // <polygon points=""> - if ( - polygonEmptyPoints && - node.name === 'polygon' && - node.attributes.points == null - ) { - removeElement(node, parentNode); - return; - } - - for (const [name, value] of Object.entries(node.attributes)) { - const ids = findReferences(name, value); - - for (const id of ids) { - allReferences.add(id); - } - } - }, - }, - root: { - exit: () => { - for (const id of removedDefIds) { - const refs = referencesById.get(id); - if (refs) { - for (const { node, parentNode } of refs) { - detachNodeFromParent(node, parentNode); - } - } - } - - if (!deoptimized) { - for (const [ - nonRenderedNode, - nonRenderedParent, - ] of nonRenderedNodes.entries()) { - const id = nonRenderedNode.attributes.id; - - if (!allReferences.has(id)) { - detachNodeFromParent(nonRenderedNode, nonRenderedParent); - } - } - } - - for (const [node, parentNode] of allDefs.entries()) { - if (node.children.length === 0) { - detachNodeFromParent(node, parentNode); - } - } - }, - }, - }; -}; diff --git a/node_modules/svgo/plugins/removeMetadata.js b/node_modules/svgo/plugins/removeMetadata.js deleted file mode 100644 index 8c9de27e..00000000 --- a/node_modules/svgo/plugins/removeMetadata.js +++ /dev/null @@ -1,27 +0,0 @@ -'use strict'; - -const { detachNodeFromParent } = require('../lib/xast.js'); - -exports.name = 'removeMetadata'; -exports.description = 'removes <metadata>'; - -/** - * Remove <metadata>. - * - * https://www.w3.org/TR/SVG11/metadata.html - * - * @author Kir Belevich - * - * @type {import('./plugins-types').Plugin<'removeMetadata'>} - */ -exports.fn = () => { - return { - element: { - enter: (node, parentNode) => { - if (node.name === 'metadata') { - detachNodeFromParent(node, parentNode); - } - }, - }, - }; -}; diff --git a/node_modules/svgo/plugins/removeNonInheritableGroupAttrs.js b/node_modules/svgo/plugins/removeNonInheritableGroupAttrs.js deleted file mode 100644 index 73e6bcda..00000000 --- a/node_modules/svgo/plugins/removeNonInheritableGroupAttrs.js +++ /dev/null @@ -1,38 +0,0 @@ -'use strict'; - -const { - inheritableAttrs, - attrsGroups, - presentationNonInheritableGroupAttrs, -} = require('./_collections'); - -exports.name = 'removeNonInheritableGroupAttrs'; -exports.description = - 'removes non-inheritable group’s presentational attributes'; - -/** - * Remove non-inheritable group's "presentation" attributes. - * - * @author Kir Belevich - * - * @type {import('./plugins-types').Plugin<'removeNonInheritableGroupAttrs'>} - */ -exports.fn = () => { - return { - element: { - enter: (node) => { - if (node.name === 'g') { - for (const name of Object.keys(node.attributes)) { - if ( - attrsGroups.presentation.has(name) && - !inheritableAttrs.has(name) && - !presentationNonInheritableGroupAttrs.has(name) - ) { - delete node.attributes[name]; - } - } - } - }, - }, - }; -}; diff --git a/node_modules/svgo/plugins/removeOffCanvasPaths.js b/node_modules/svgo/plugins/removeOffCanvasPaths.js deleted file mode 100644 index 60a69fb1..00000000 --- a/node_modules/svgo/plugins/removeOffCanvasPaths.js +++ /dev/null @@ -1,136 +0,0 @@ -'use strict'; - -/** - * @typedef {import('../lib/types').PathDataItem} PathDataItem - */ - -const { visitSkip, detachNodeFromParent } = require('../lib/xast.js'); -const { parsePathData } = require('../lib/path.js'); -const { intersects } = require('./_path.js'); - -exports.name = 'removeOffCanvasPaths'; -exports.description = - 'removes elements that are drawn outside of the viewbox (disabled by default)'; - -/** - * Remove elements that are drawn outside of the viewbox. - * - * @author JoshyPHP - * - * @type {import('./plugins-types').Plugin<'removeOffCanvasPaths'>} - */ -exports.fn = () => { - /** - * @type {?{ - * top: number, - * right: number, - * bottom: number, - * left: number, - * width: number, - * height: number - * }} - */ - let viewBoxData = null; - - return { - element: { - enter: (node, parentNode) => { - if (node.name === 'svg' && parentNode.type === 'root') { - let viewBox = ''; - // find viewbox - if (node.attributes.viewBox != null) { - // remove commas and plus signs, normalize and trim whitespace - viewBox = node.attributes.viewBox; - } else if ( - node.attributes.height != null && - node.attributes.width != null - ) { - viewBox = `0 0 ${node.attributes.width} ${node.attributes.height}`; - } - - // parse viewbox - // remove commas and plus signs, normalize and trim whitespace - viewBox = viewBox - .replace(/[,+]|px/g, ' ') - .replace(/\s+/g, ' ') - .replace(/^\s*|\s*$/g, ''); - // ensure that the dimensions are 4 values separated by space - const m = - /^(-?\d*\.?\d+) (-?\d*\.?\d+) (\d*\.?\d+) (\d*\.?\d+)$/.exec( - viewBox, - ); - if (m == null) { - return; - } - const left = Number.parseFloat(m[1]); - const top = Number.parseFloat(m[2]); - const width = Number.parseFloat(m[3]); - const height = Number.parseFloat(m[4]); - - // store the viewBox boundaries - viewBoxData = { - left, - top, - right: left + width, - bottom: top + height, - width, - height, - }; - } - - // consider that any item with a transform attribute is visible - if (node.attributes.transform != null) { - return visitSkip; - } - - if ( - node.name === 'path' && - node.attributes.d != null && - viewBoxData != null - ) { - const pathData = parsePathData(node.attributes.d); - - // consider that a M command within the viewBox is visible - let visible = false; - for (const pathDataItem of pathData) { - if (pathDataItem.command === 'M') { - const [x, y] = pathDataItem.args; - if ( - x >= viewBoxData.left && - x <= viewBoxData.right && - y >= viewBoxData.top && - y <= viewBoxData.bottom - ) { - visible = true; - } - } - } - if (visible) { - return; - } - - if (pathData.length === 2) { - // close the path too short for intersects() - pathData.push({ command: 'z', args: [] }); - } - - const { left, top, width, height } = viewBoxData; - /** - * @type {PathDataItem[]} - */ - const viewBoxPathData = [ - { command: 'M', args: [left, top] }, - { command: 'h', args: [width] }, - { command: 'v', args: [height] }, - { command: 'H', args: [left] }, - { command: 'z', args: [] }, - ]; - - if (intersects(viewBoxPathData, pathData) === false) { - detachNodeFromParent(node, parentNode); - } - } - }, - }, - }; -}; diff --git a/node_modules/svgo/plugins/removeRasterImages.js b/node_modules/svgo/plugins/removeRasterImages.js deleted file mode 100644 index 1a4a4a12..00000000 --- a/node_modules/svgo/plugins/removeRasterImages.js +++ /dev/null @@ -1,31 +0,0 @@ -'use strict'; - -const { detachNodeFromParent } = require('../lib/xast.js'); - -exports.name = 'removeRasterImages'; -exports.description = 'removes raster images (disabled by default)'; - -/** - * Remove raster images references in <image>. - * - * @see https://bugs.webkit.org/show_bug.cgi?id=63548 - * - * @author Kir Belevich - * - * @type {import('./plugins-types').Plugin<'removeRasterImages'>} - */ -exports.fn = () => { - return { - element: { - enter: (node, parentNode) => { - if ( - node.name === 'image' && - node.attributes['xlink:href'] != null && - /(\.|image\/)(jpe?g|png|gif)/.test(node.attributes['xlink:href']) - ) { - detachNodeFromParent(node, parentNode); - } - }, - }, - }; -}; diff --git a/node_modules/svgo/plugins/removeScriptElement.js b/node_modules/svgo/plugins/removeScriptElement.js deleted file mode 100644 index 80fcea3b..00000000 --- a/node_modules/svgo/plugins/removeScriptElement.js +++ /dev/null @@ -1,70 +0,0 @@ -'use strict'; - -const { detachNodeFromParent } = require('../lib/xast.js'); -const { attrsGroups } = require('./_collections.js'); - -exports.name = 'removeScriptElement'; -exports.description = 'removes scripts (disabled by default)'; - -/** Union of all event attributes. */ -const eventAttrs = [ - ...attrsGroups.animationEvent, - ...attrsGroups.documentEvent, - ...attrsGroups.documentElementEvent, - ...attrsGroups.globalEvent, - ...attrsGroups.graphicalEvent, -]; - -/** - * Remove scripts. - * - * https://www.w3.org/TR/SVG11/script.html - * - * @author Patrick Klingemann - * @type {import('./plugins-types').Plugin<'removeScriptElement'>} - */ -exports.fn = () => { - return { - element: { - enter: (node, parentNode) => { - if (node.name === 'script') { - detachNodeFromParent(node, parentNode); - return; - } - - for (const attr of eventAttrs) { - if (node.attributes[attr] != null) { - delete node.attributes[attr]; - } - } - }, - exit: (node, parentNode) => { - if (node.name !== 'a') { - return; - } - - for (const attr of Object.keys(node.attributes)) { - if (attr === 'href' || attr.endsWith(':href')) { - if ( - node.attributes[attr] == null || - !node.attributes[attr].trimStart().startsWith('javascript:') - ) { - continue; - } - - const index = parentNode.children.indexOf(node); - parentNode.children.splice(index, 1, ...node.children); - - // TODO remove legacy parentNode in v4 - for (const child of node.children) { - Object.defineProperty(child, 'parentNode', { - writable: true, - value: parentNode, - }); - } - } - } - }, - }, - }; -}; diff --git a/node_modules/svgo/plugins/removeStyleElement.js b/node_modules/svgo/plugins/removeStyleElement.js deleted file mode 100644 index d869a50a..00000000 --- a/node_modules/svgo/plugins/removeStyleElement.js +++ /dev/null @@ -1,27 +0,0 @@ -'use strict'; - -const { detachNodeFromParent } = require('../lib/xast.js'); - -exports.name = 'removeStyleElement'; -exports.description = 'removes <style> element (disabled by default)'; - -/** - * Remove <style>. - * - * https://www.w3.org/TR/SVG11/styling.html#StyleElement - * - * @author Betsy Dupuis - * - * @type {import('./plugins-types').Plugin<'removeStyleElement'>} - */ -exports.fn = () => { - return { - element: { - enter: (node, parentNode) => { - if (node.name === 'style') { - detachNodeFromParent(node, parentNode); - } - }, - }, - }; -}; diff --git a/node_modules/svgo/plugins/removeTitle.js b/node_modules/svgo/plugins/removeTitle.js deleted file mode 100644 index 906dd3c2..00000000 --- a/node_modules/svgo/plugins/removeTitle.js +++ /dev/null @@ -1,27 +0,0 @@ -'use strict'; - -const { detachNodeFromParent } = require('../lib/xast.js'); - -exports.name = 'removeTitle'; -exports.description = 'removes <title>'; - -/** - * Remove <title>. - * - * https://developer.mozilla.org/docs/Web/SVG/Element/title - * - * @author Igor Kalashnikov - * - * @type {import('./plugins-types').Plugin<'removeTitle'>} - */ -exports.fn = () => { - return { - element: { - enter: (node, parentNode) => { - if (node.name === 'title') { - detachNodeFromParent(node, parentNode); - } - }, - }, - }; -}; diff --git a/node_modules/svgo/plugins/removeUnknownsAndDefaults.js b/node_modules/svgo/plugins/removeUnknownsAndDefaults.js deleted file mode 100644 index e76e4e5f..00000000 --- a/node_modules/svgo/plugins/removeUnknownsAndDefaults.js +++ /dev/null @@ -1,212 +0,0 @@ -'use strict'; - -const { visitSkip, detachNodeFromParent } = require('../lib/xast.js'); -const { collectStylesheet, computeStyle } = require('../lib/style.js'); -const { - elems, - attrsGroups, - elemsGroups, - attrsGroupsDefaults, - presentationNonInheritableGroupAttrs, -} = require('./_collections'); - -exports.name = 'removeUnknownsAndDefaults'; -exports.description = - 'removes unknown elements content and attributes, removes attrs with default values'; - -// resolve all groups references - -/** - * @type {Map<string, Set<string>>} - */ -const allowedChildrenPerElement = new Map(); -/** - * @type {Map<string, Set<string>>} - */ -const allowedAttributesPerElement = new Map(); -/** - * @type {Map<string, Map<string, string>>} - */ -const attributesDefaultsPerElement = new Map(); - -for (const [name, config] of Object.entries(elems)) { - /** - * @type {Set<string>} - */ - const allowedChildren = new Set(); - if (config.content) { - for (const elementName of config.content) { - allowedChildren.add(elementName); - } - } - if (config.contentGroups) { - for (const contentGroupName of config.contentGroups) { - const elemsGroup = elemsGroups[contentGroupName]; - if (elemsGroup) { - for (const elementName of elemsGroup) { - allowedChildren.add(elementName); - } - } - } - } - /** - * @type {Set<string>} - */ - const allowedAttributes = new Set(); - if (config.attrs) { - for (const attrName of config.attrs) { - allowedAttributes.add(attrName); - } - } - /** - * @type {Map<string, string>} - */ - const attributesDefaults = new Map(); - if (config.defaults) { - for (const [attrName, defaultValue] of Object.entries(config.defaults)) { - attributesDefaults.set(attrName, defaultValue); - } - } - for (const attrsGroupName of config.attrsGroups) { - const attrsGroup = attrsGroups[attrsGroupName]; - if (attrsGroup) { - for (const attrName of attrsGroup) { - allowedAttributes.add(attrName); - } - } - const groupDefaults = attrsGroupsDefaults[attrsGroupName]; - if (groupDefaults) { - for (const [attrName, defaultValue] of Object.entries(groupDefaults)) { - attributesDefaults.set(attrName, defaultValue); - } - } - } - allowedChildrenPerElement.set(name, allowedChildren); - allowedAttributesPerElement.set(name, allowedAttributes); - attributesDefaultsPerElement.set(name, attributesDefaults); -} - -/** - * Remove unknown elements content and attributes, - * remove attributes with default values. - * - * @author Kir Belevich - * - * @type {import('./plugins-types').Plugin<'removeUnknownsAndDefaults'>} - */ -exports.fn = (root, params) => { - const { - unknownContent = true, - unknownAttrs = true, - defaultAttrs = true, - defaultMarkupDeclarations = true, - uselessOverrides = true, - keepDataAttrs = true, - keepAriaAttrs = true, - keepRoleAttr = false, - } = params; - const stylesheet = collectStylesheet(root); - - return { - instruction: { - enter: (node) => { - if (defaultMarkupDeclarations) { - node.value = node.value.replace(/\s*standalone\s*=\s*(["'])no\1/, ''); - } - }, - }, - element: { - enter: (node, parentNode) => { - // skip namespaced elements - if (node.name.includes(':')) { - return; - } - // skip visiting foreignObject subtree - if (node.name === 'foreignObject') { - return visitSkip; - } - - // remove unknown element's content - if (unknownContent && parentNode.type === 'element') { - const allowedChildren = allowedChildrenPerElement.get( - parentNode.name, - ); - if (allowedChildren == null || allowedChildren.size === 0) { - // remove unknown elements - if (allowedChildrenPerElement.get(node.name) == null) { - detachNodeFromParent(node, parentNode); - return; - } - } else { - // remove not allowed children - if (allowedChildren.has(node.name) === false) { - detachNodeFromParent(node, parentNode); - return; - } - } - } - - const allowedAttributes = allowedAttributesPerElement.get(node.name); - const attributesDefaults = attributesDefaultsPerElement.get(node.name); - const computedParentStyle = - parentNode.type === 'element' - ? computeStyle(stylesheet, parentNode) - : null; - - // remove element's unknown attrs and attrs with default values - for (const [name, value] of Object.entries(node.attributes)) { - if (keepDataAttrs && name.startsWith('data-')) { - continue; - } - if (keepAriaAttrs && name.startsWith('aria-')) { - continue; - } - if (keepRoleAttr && name === 'role') { - continue; - } - // skip xmlns attribute - if (name === 'xmlns') { - continue; - } - // skip namespaced attributes except xml:* and xlink:* - if (name.includes(':')) { - const [prefix] = name.split(':'); - if (prefix !== 'xml' && prefix !== 'xlink') { - continue; - } - } - - if ( - unknownAttrs && - allowedAttributes && - allowedAttributes.has(name) === false - ) { - delete node.attributes[name]; - } - if ( - defaultAttrs && - node.attributes.id == null && - attributesDefaults && - attributesDefaults.get(name) === value - ) { - // keep defaults if parent has own or inherited style - if (computedParentStyle?.[name] == null) { - delete node.attributes[name]; - } - } - if (uselessOverrides && node.attributes.id == null) { - const style = computedParentStyle?.[name]; - if ( - presentationNonInheritableGroupAttrs.has(name) === false && - style != null && - style.type === 'static' && - style.value === value - ) { - delete node.attributes[name]; - } - } - } - }, - }, - }; -}; diff --git a/node_modules/svgo/plugins/removeUnusedNS.js b/node_modules/svgo/plugins/removeUnusedNS.js deleted file mode 100644 index ac81eeaf..00000000 --- a/node_modules/svgo/plugins/removeUnusedNS.js +++ /dev/null @@ -1,59 +0,0 @@ -'use strict'; - -exports.name = 'removeUnusedNS'; -exports.description = 'removes unused namespaces declaration'; - -/** - * Remove unused namespaces declaration from svg element - * which are not used in elements or attributes - * - * @author Kir Belevich - * - * @type {import('./plugins-types').Plugin<'removeUnusedNS'>} - */ -exports.fn = () => { - /** - * @type {Set<string>} - */ - const unusedNamespaces = new Set(); - return { - element: { - enter: (node, parentNode) => { - // collect all namespaces from svg element - // (such as xmlns:xlink="http://www.w3.org/1999/xlink") - if (node.name === 'svg' && parentNode.type === 'root') { - for (const name of Object.keys(node.attributes)) { - if (name.startsWith('xmlns:')) { - const local = name.slice('xmlns:'.length); - unusedNamespaces.add(local); - } - } - } - if (unusedNamespaces.size !== 0) { - // preserve namespace used in nested elements names - if (node.name.includes(':')) { - const [ns] = node.name.split(':'); - if (unusedNamespaces.has(ns)) { - unusedNamespaces.delete(ns); - } - } - // preserve namespace used in nested elements attributes - for (const name of Object.keys(node.attributes)) { - if (name.includes(':')) { - const [ns] = name.split(':'); - unusedNamespaces.delete(ns); - } - } - } - }, - exit: (node, parentNode) => { - // remove unused namespace attributes from svg element - if (node.name === 'svg' && parentNode.type === 'root') { - for (const name of unusedNamespaces) { - delete node.attributes[`xmlns:${name}`]; - } - } - }, - }, - }; -}; diff --git a/node_modules/svgo/plugins/removeUselessDefs.js b/node_modules/svgo/plugins/removeUselessDefs.js deleted file mode 100644 index 5d19810e..00000000 --- a/node_modules/svgo/plugins/removeUselessDefs.js +++ /dev/null @@ -1,65 +0,0 @@ -'use strict'; - -/** - * @typedef {import('../lib/types').XastElement} XastElement - */ - -const { detachNodeFromParent } = require('../lib/xast.js'); -const { elemsGroups } = require('./_collections.js'); - -exports.name = 'removeUselessDefs'; -exports.description = 'removes elements in <defs> without id'; - -/** - * Removes content of defs and properties that aren't rendered directly without ids. - * - * @author Lev Solntsev - * - * @type {import('./plugins-types').Plugin<'removeUselessDefs'>} - */ -exports.fn = () => { - return { - element: { - enter: (node, parentNode) => { - if (node.name === 'defs') { - /** - * @type {XastElement[]} - */ - const usefulNodes = []; - collectUsefulNodes(node, usefulNodes); - if (usefulNodes.length === 0) { - detachNodeFromParent(node, parentNode); - } - // TODO remove legacy parentNode in v4 - for (const usefulNode of usefulNodes) { - Object.defineProperty(usefulNode, 'parentNode', { - writable: true, - value: node, - }); - } - node.children = usefulNodes; - } else if ( - elemsGroups.nonRendering.has(node.name) && - node.attributes.id == null - ) { - detachNodeFromParent(node, parentNode); - } - }, - }, - }; -}; - -/** - * @type {(node: XastElement, usefulNodes: XastElement[]) => void} - */ -const collectUsefulNodes = (node, usefulNodes) => { - for (const child of node.children) { - if (child.type === 'element') { - if (child.attributes.id != null || child.name === 'style') { - usefulNodes.push(child); - } else { - collectUsefulNodes(child, usefulNodes); - } - } - } -}; diff --git a/node_modules/svgo/plugins/removeUselessStrokeAndFill.js b/node_modules/svgo/plugins/removeUselessStrokeAndFill.js deleted file mode 100644 index 95193fa6..00000000 --- a/node_modules/svgo/plugins/removeUselessStrokeAndFill.js +++ /dev/null @@ -1,139 +0,0 @@ -'use strict'; - -const { visit, visitSkip, detachNodeFromParent } = require('../lib/xast.js'); -const { collectStylesheet, computeStyle } = require('../lib/style.js'); -const { hasScripts } = require('../lib/svgo/tools.js'); -const { elemsGroups } = require('./_collections.js'); - -exports.name = 'removeUselessStrokeAndFill'; -exports.description = 'removes useless stroke and fill attributes'; - -/** - * Remove useless stroke and fill attrs. - * - * @author Kir Belevich - * - * @type {import('./plugins-types').Plugin<'removeUselessStrokeAndFill'>} - */ -exports.fn = (root, params) => { - const { - stroke: removeStroke = true, - fill: removeFill = true, - removeNone = false, - } = params; - - // style and script elements deoptimise this plugin - let hasStyleOrScript = false; - visit(root, { - element: { - enter: (node) => { - if (node.name === 'style' || hasScripts(node)) { - hasStyleOrScript = true; - } - }, - }, - }); - if (hasStyleOrScript) { - return null; - } - - const stylesheet = collectStylesheet(root); - - return { - element: { - enter: (node, parentNode) => { - // id attribute deoptimise the whole subtree - if (node.attributes.id != null) { - return visitSkip; - } - if (!elemsGroups.shape.has(node.name)) { - return; - } - const computedStyle = computeStyle(stylesheet, node); - const stroke = computedStyle.stroke; - const strokeOpacity = computedStyle['stroke-opacity']; - const strokeWidth = computedStyle['stroke-width']; - const markerEnd = computedStyle['marker-end']; - const fill = computedStyle.fill; - const fillOpacity = computedStyle['fill-opacity']; - const computedParentStyle = - parentNode.type === 'element' - ? computeStyle(stylesheet, parentNode) - : null; - const parentStroke = - computedParentStyle == null ? null : computedParentStyle.stroke; - - // remove stroke* - if (removeStroke) { - if ( - stroke == null || - (stroke.type === 'static' && stroke.value == 'none') || - (strokeOpacity != null && - strokeOpacity.type === 'static' && - strokeOpacity.value === '0') || - (strokeWidth != null && - strokeWidth.type === 'static' && - strokeWidth.value === '0') - ) { - // stroke-width may affect the size of marker-end - // marker is not visible when stroke-width is 0 - if ( - (strokeWidth != null && - strokeWidth.type === 'static' && - strokeWidth.value === '0') || - markerEnd == null - ) { - for (const name of Object.keys(node.attributes)) { - if (name.startsWith('stroke')) { - delete node.attributes[name]; - } - } - // set explicit none to not inherit from parent - if ( - parentStroke != null && - parentStroke.type === 'static' && - parentStroke.value !== 'none' - ) { - node.attributes.stroke = 'none'; - } - } - } - } - - // remove fill* - if (removeFill) { - if ( - (fill != null && fill.type === 'static' && fill.value === 'none') || - (fillOpacity != null && - fillOpacity.type === 'static' && - fillOpacity.value === '0') - ) { - for (const name of Object.keys(node.attributes)) { - if (name.startsWith('fill-')) { - delete node.attributes[name]; - } - } - if ( - fill == null || - (fill.type === 'static' && fill.value !== 'none') - ) { - node.attributes.fill = 'none'; - } - } - } - - if (removeNone) { - if ( - (stroke == null || node.attributes.stroke === 'none') && - ((fill != null && - fill.type === 'static' && - fill.value === 'none') || - node.attributes.fill === 'none') - ) { - detachNodeFromParent(node, parentNode); - } - } - }, - }, - }; -}; diff --git a/node_modules/svgo/plugins/removeViewBox.js b/node_modules/svgo/plugins/removeViewBox.js deleted file mode 100644 index c0f23de2..00000000 --- a/node_modules/svgo/plugins/removeViewBox.js +++ /dev/null @@ -1,49 +0,0 @@ -'use strict'; - -exports.name = 'removeViewBox'; -exports.description = 'removes viewBox attribute when possible'; - -const viewBoxElems = new Set(['pattern', 'svg', 'symbol']); - -/** - * Remove viewBox attr which coincides with a width/height box. - * - * @see https://www.w3.org/TR/SVG11/coords.html#ViewBoxAttribute - * - * @example - * <svg width="100" height="50" viewBox="0 0 100 50"> - * ⬇ - * <svg width="100" height="50"> - * - * @author Kir Belevich - * - * @type {import('./plugins-types').Plugin<'removeViewBox'>} - */ -exports.fn = () => { - return { - element: { - enter: (node, parentNode) => { - if ( - viewBoxElems.has(node.name) && - node.attributes.viewBox != null && - node.attributes.width != null && - node.attributes.height != null - ) { - // TODO remove width/height for such case instead - if (node.name === 'svg' && parentNode.type !== 'root') { - return; - } - const nums = node.attributes.viewBox.split(/[ ,]+/g); - if ( - nums[0] === '0' && - nums[1] === '0' && - node.attributes.width.replace(/px$/, '') === nums[2] && // could use parseFloat too - node.attributes.height.replace(/px$/, '') === nums[3] - ) { - delete node.attributes.viewBox; - } - } - }, - }, - }; -}; diff --git a/node_modules/svgo/plugins/removeXMLNS.js b/node_modules/svgo/plugins/removeXMLNS.js deleted file mode 100644 index b7ec69a1..00000000 --- a/node_modules/svgo/plugins/removeXMLNS.js +++ /dev/null @@ -1,29 +0,0 @@ -'use strict'; - -exports.name = 'removeXMLNS'; -exports.description = - 'removes xmlns attribute (for inline svg, disabled by default)'; - -/** - * Remove the xmlns attribute when present. - * - * @example - * <svg viewBox="0 0 100 50" xmlns="http://www.w3.org/2000/svg"> - * ↓ - * <svg viewBox="0 0 100 50"> - * - * @author Ricardo Tomasi - * - * @type {import('./plugins-types').Plugin<'removeXMLNS'>} - */ -exports.fn = () => { - return { - element: { - enter: (node) => { - if (node.name === 'svg') { - delete node.attributes.xmlns; - } - }, - }, - }; -}; diff --git a/node_modules/svgo/plugins/removeXMLProcInst.js b/node_modules/svgo/plugins/removeXMLProcInst.js deleted file mode 100644 index 73ec2335..00000000 --- a/node_modules/svgo/plugins/removeXMLProcInst.js +++ /dev/null @@ -1,28 +0,0 @@ -'use strict'; - -const { detachNodeFromParent } = require('../lib/xast.js'); - -exports.name = 'removeXMLProcInst'; -exports.description = 'removes XML processing instructions'; - -/** - * Remove XML Processing Instruction. - * - * @example - * <?xml version="1.0" encoding="utf-8"?> - * - * @author Kir Belevich - * - * @type {import('./plugins-types').Plugin<'removeXMLProcInst'>} - */ -exports.fn = () => { - return { - instruction: { - enter: (node, parentNode) => { - if (node.name === 'xml') { - detachNodeFromParent(node, parentNode); - } - }, - }, - }; -}; diff --git a/node_modules/svgo/plugins/removeXlink.js b/node_modules/svgo/plugins/removeXlink.js deleted file mode 100644 index 0411cc14..00000000 --- a/node_modules/svgo/plugins/removeXlink.js +++ /dev/null @@ -1,226 +0,0 @@ -'use strict'; - -const { elems } = require('./_collections'); - -/** - * @typedef {import('../lib/types').XastElement} XastElement - */ - -exports.name = 'removeXlink'; -exports.description = - 'remove xlink namespace and replaces attributes with the SVG 2 equivalent where applicable'; - -/** URI indicating the Xlink namespace. */ -const XLINK_NAMESPACE = 'http://www.w3.org/1999/xlink'; - -/** - * Map of `xlink:show` values to the SVG 2 `target` attribute values. - * - * @type {Record<string, string>} - * @see https://developer.mozilla.org/docs/Web/SVG/Attribute/xlink:show#usage_notes - */ -const SHOW_TO_TARGET = { - new: '_blank', - replace: '_self', -}; - -/** - * Elements that use xlink:href, but were deprecated in SVG 2 and therefore - * don't support the SVG 2 href attribute. - * - * @type {Set<string>} - * @see https://developer.mozilla.org/docs/Web/SVG/Attribute/xlink:href - * @see https://developer.mozilla.org/docs/Web/SVG/Attribute/href - */ -const LEGACY_ELEMENTS = new Set([ - 'cursor', - 'filter', - 'font-face-uri', - 'glyphRef', - 'tref', -]); - -/** - * @param {XastElement} node - * @param {string[]} prefixes - * @param {string} attr - * @returns {string[]} - */ -const findPrefixedAttrs = (node, prefixes, attr) => { - return prefixes - .map((prefix) => `${prefix}:${attr}`) - .filter((attr) => node.attributes[attr] != null); -}; - -/** - * Removes XLink namespace prefixes and converts references to XLink attributes - * to the native SVG equivalent. - * - * The XLink namespace is deprecated in SVG 2. - * - * @type {import('./plugins-types').Plugin<'removeXlink'>} - * @see https://developer.mozilla.org/docs/Web/SVG/Attribute/xlink:href - */ -exports.fn = (_, params) => { - const { includeLegacy } = params; - - /** - * XLink namespace prefixes that are currently in the stack. - * - * @type {string[]} - */ - const xlinkPrefixes = []; - - /** - * Namespace prefixes that exist in {@link xlinkPrefixes} but were overridden - * in a child element to point to another namespace, and so is not treated as - * an XLink attribute. - * - * @type {string[]} - */ - const overriddenPrefixes = []; - - /** - * Namespace prefixes that were used in one of the {@link LEGACY_ELEMENTS}. - * - * @type {string[]} - */ - const usedInLegacyElement = []; - - return { - element: { - enter: (node) => { - for (const [key, value] of Object.entries(node.attributes)) { - if (key.startsWith('xmlns:')) { - const prefix = key.split(':', 2)[1]; - - if (value === XLINK_NAMESPACE) { - xlinkPrefixes.push(prefix); - continue; - } - - if (xlinkPrefixes.includes(prefix)) { - overriddenPrefixes.push(prefix); - } - } - } - - if ( - overriddenPrefixes.some((prefix) => xlinkPrefixes.includes(prefix)) - ) { - return; - } - - const showAttrs = findPrefixedAttrs(node, xlinkPrefixes, 'show'); - let showHandled = node.attributes.target != null; - for (let i = showAttrs.length - 1; i >= 0; i--) { - const attr = showAttrs[i]; - const value = node.attributes[attr]; - const mapping = SHOW_TO_TARGET[value]; - - if (showHandled || mapping == null) { - delete node.attributes[attr]; - continue; - } - - if (mapping !== elems[node.name]?.defaults?.target) { - node.attributes.target = mapping; - } - - delete node.attributes[attr]; - showHandled = true; - } - - const titleAttrs = findPrefixedAttrs(node, xlinkPrefixes, 'title'); - for (let i = titleAttrs.length - 1; i >= 0; i--) { - const attr = titleAttrs[i]; - const value = node.attributes[attr]; - const hasTitle = node.children.filter( - (child) => child.type === 'element' && child.name === 'title', - ); - - if (hasTitle.length > 0) { - delete node.attributes[attr]; - continue; - } - - /** @type {XastElement} */ - const titleTag = { - type: 'element', - name: 'title', - attributes: {}, - children: [ - { - type: 'text', - value, - }, - ], - }; - - Object.defineProperty(titleTag, 'parentNode', { - writable: true, - value: node, - }); - - node.children.unshift(titleTag); - delete node.attributes[attr]; - } - - const hrefAttrs = findPrefixedAttrs(node, xlinkPrefixes, 'href'); - - if ( - hrefAttrs.length > 0 && - LEGACY_ELEMENTS.has(node.name) && - !includeLegacy - ) { - hrefAttrs - .map((attr) => attr.split(':', 1)[0]) - .forEach((prefix) => usedInLegacyElement.push(prefix)); - return; - } - - for (let i = hrefAttrs.length - 1; i >= 0; i--) { - const attr = hrefAttrs[i]; - const value = node.attributes[attr]; - - if (node.attributes.href != null) { - delete node.attributes[attr]; - continue; - } - - node.attributes.href = value; - delete node.attributes[attr]; - } - }, - exit: (node) => { - for (const [key, value] of Object.entries(node.attributes)) { - const [prefix, attr] = key.split(':', 2); - - if ( - xlinkPrefixes.includes(prefix) && - !overriddenPrefixes.includes(prefix) && - !usedInLegacyElement.includes(prefix) && - !includeLegacy - ) { - delete node.attributes[key]; - continue; - } - - if (key.startsWith('xmlns:') && !usedInLegacyElement.includes(attr)) { - if (value === XLINK_NAMESPACE) { - const index = xlinkPrefixes.indexOf(attr); - xlinkPrefixes.splice(index, 1); - delete node.attributes[key]; - continue; - } - - if (overriddenPrefixes.includes(prefix)) { - const index = overriddenPrefixes.indexOf(attr); - overriddenPrefixes.splice(index, 1); - } - } - } - }, - }, - }; -}; diff --git a/node_modules/svgo/plugins/reusePaths.js b/node_modules/svgo/plugins/reusePaths.js deleted file mode 100644 index 8c2ddf0d..00000000 --- a/node_modules/svgo/plugins/reusePaths.js +++ /dev/null @@ -1,195 +0,0 @@ -'use strict'; - -const { collectStylesheet } = require('../lib/style'); -const { detachNodeFromParent, querySelectorAll } = require('../lib/xast'); - -/** - * @typedef {import('../lib/types').XastElement} XastElement - * @typedef {import('../lib/types').XastParent} XastParent - * @typedef {import('../lib/types').XastNode} XastNode - */ - -exports.name = 'reusePaths'; -exports.description = - 'Finds <path> elements with the same d, fill, and ' + - 'stroke, and converts them to <use> elements ' + - 'referencing a single <path> def.'; - -/** - * Finds <path> elements with the same d, fill, and stroke, and converts them to - * <use> elements referencing a single <path> def. - * - * @author Jacob Howcroft - * - * @type {import('./plugins-types').Plugin<'reusePaths'>} - */ -exports.fn = (root) => { - const stylesheet = collectStylesheet(root); - - /** - * @type {Map<string, XastElement[]>} - */ - const paths = new Map(); - - /** - * Reference to the first defs element that is a direct child of the svg - * element if one exists. - * - * @type {XastElement} - * @see https://developer.mozilla.org/docs/Web/SVG/Element/defs - */ - let svgDefs; - - /** - * Set of hrefs that reference the id of another node. - * - * @type {Set<string>} - */ - const hrefs = new Set(); - - return { - element: { - enter: (node, parentNode) => { - if (node.name === 'path' && node.attributes.d != null) { - const d = node.attributes.d; - const fill = node.attributes.fill || ''; - const stroke = node.attributes.stroke || ''; - const key = d + ';s:' + stroke + ';f:' + fill; - let list = paths.get(key); - if (list == null) { - list = []; - paths.set(key, list); - } - list.push(node); - } - - if ( - svgDefs == null && - node.name === 'defs' && - parentNode.type === 'element' && - parentNode.name === 'svg' - ) { - svgDefs = node; - } - - if (node.name === 'use') { - for (const name of ['href', 'xlink:href']) { - const href = node.attributes[name]; - - if (href != null && href.startsWith('#') && href.length > 1) { - hrefs.add(href.slice(1)); - } - } - } - }, - - exit: (node, parentNode) => { - if (node.name === 'svg' && parentNode.type === 'root') { - let defsTag = svgDefs; - - if (defsTag == null) { - defsTag = { - type: 'element', - name: 'defs', - attributes: {}, - children: [], - }; - // TODO remove legacy parentNode in v4 - Object.defineProperty(defsTag, 'parentNode', { - writable: true, - value: node, - }); - } - - let index = 0; - for (const list of paths.values()) { - if (list.length > 1) { - /** @type {XastElement} */ - const reusablePath = { - type: 'element', - name: 'path', - attributes: {}, - children: [], - }; - - for (const attr of ['fill', 'stroke', 'd']) { - if (list[0].attributes[attr] != null) { - reusablePath.attributes[attr] = list[0].attributes[attr]; - } - } - - const originalId = list[0].attributes.id; - if ( - originalId == null || - hrefs.has(originalId) || - stylesheet.rules.some( - (rule) => rule.selector === `#${originalId}`, - ) - ) { - reusablePath.attributes.id = 'reuse-' + index++; - } else { - reusablePath.attributes.id = originalId; - delete list[0].attributes.id; - } - // TODO remove legacy parentNode in v4 - Object.defineProperty(reusablePath, 'parentNode', { - writable: true, - value: defsTag, - }); - defsTag.children.push(reusablePath); - // convert paths to <use> - for (const pathNode of list) { - delete pathNode.attributes.d; - delete pathNode.attributes.stroke; - delete pathNode.attributes.fill; - - if ( - defsTag.children.includes(pathNode) && - pathNode.children.length === 0 - ) { - if (Object.keys(pathNode.attributes).length === 0) { - detachNodeFromParent(pathNode, defsTag); - continue; - } - - if ( - Object.keys(pathNode.attributes).length === 1 && - pathNode.attributes.id != null - ) { - detachNodeFromParent(pathNode, defsTag); - const selector = `[xlink\\:href=#${pathNode.attributes.id}], [href=#${pathNode.attributes.id}]`; - for (const child of querySelectorAll(node, selector)) { - if (child.type !== 'element') { - continue; - } - for (const name of ['href', 'xlink:href']) { - if (child.attributes[name] != null) { - child.attributes[name] = - '#' + reusablePath.attributes.id; - } - } - } - continue; - } - } - - pathNode.name = 'use'; - pathNode.attributes['xlink:href'] = - '#' + reusablePath.attributes.id; - } - } - } - if (defsTag.children.length !== 0) { - if (node.attributes['xmlns:xlink'] == null) { - node.attributes['xmlns:xlink'] = 'http://www.w3.org/1999/xlink'; - } - - if (svgDefs == null) { - node.children.unshift(defsTag); - } - } - } - }, - }, - }; -}; diff --git a/node_modules/svgo/plugins/sortAttrs.js b/node_modules/svgo/plugins/sortAttrs.js deleted file mode 100644 index 6347f65e..00000000 --- a/node_modules/svgo/plugins/sortAttrs.js +++ /dev/null @@ -1,108 +0,0 @@ -'use strict'; - -exports.name = 'sortAttrs'; -exports.description = 'Sort element attributes for better compression'; - -/** - * Sort element attributes for better compression - * - * @author Nikolay Frantsev - * - * @type {import('./plugins-types').Plugin<'sortAttrs'>} - */ -exports.fn = (_root, params) => { - const { - order = [ - 'id', - 'width', - 'height', - 'x', - 'x1', - 'x2', - 'y', - 'y1', - 'y2', - 'cx', - 'cy', - 'r', - 'fill', - 'stroke', - 'marker', - 'd', - 'points', - ], - xmlnsOrder = 'front', - } = params; - - /** - * @type {(name: string) => number} - */ - const getNsPriority = (name) => { - if (xmlnsOrder === 'front') { - // put xmlns first - if (name === 'xmlns') { - return 3; - } - // xmlns:* attributes second - if (name.startsWith('xmlns:')) { - return 2; - } - } - // other namespaces after and sort them alphabetically - if (name.includes(':')) { - return 1; - } - // other attributes - return 0; - }; - - /** - * @type {(a: [string, string], b: [string, string]) => number} - */ - const compareAttrs = ([aName], [bName]) => { - // sort namespaces - const aPriority = getNsPriority(aName); - const bPriority = getNsPriority(bName); - const priorityNs = bPriority - aPriority; - if (priorityNs !== 0) { - return priorityNs; - } - // extract the first part from attributes - // for example "fill" from "fill" and "fill-opacity" - const [aPart] = aName.split('-'); - const [bPart] = bName.split('-'); - // rely on alphabetical sort when the first part is the same - if (aPart !== bPart) { - const aInOrderFlag = order.includes(aPart) ? 1 : 0; - const bInOrderFlag = order.includes(bPart) ? 1 : 0; - // sort by position in order param - if (aInOrderFlag === 1 && bInOrderFlag === 1) { - return order.indexOf(aPart) - order.indexOf(bPart); - } - // put attributes from order param before others - const priorityOrder = bInOrderFlag - aInOrderFlag; - if (priorityOrder !== 0) { - return priorityOrder; - } - } - // sort alphabetically - return aName < bName ? -1 : 1; - }; - - return { - element: { - enter: (node) => { - const attrs = Object.entries(node.attributes); - attrs.sort(compareAttrs); - /** - * @type {Record<string, string>} - */ - const sortedAttributes = {}; - for (const [name, value] of attrs) { - sortedAttributes[name] = value; - } - node.attributes = sortedAttributes; - }, - }, - }; -}; diff --git a/node_modules/svgo/plugins/sortDefsChildren.js b/node_modules/svgo/plugins/sortDefsChildren.js deleted file mode 100644 index 6ae5d421..00000000 --- a/node_modules/svgo/plugins/sortDefsChildren.js +++ /dev/null @@ -1,58 +0,0 @@ -'use strict'; - -exports.name = 'sortDefsChildren'; -exports.description = 'Sorts children of <defs> to improve compression'; - -/** - * Sorts children of defs in order to improve compression. - * Sorted first by frequency then by element name length then by element name (to ensure grouping). - * - * @author David Leston - * - * @type {import('./plugins-types').Plugin<'sortDefsChildren'>} - */ -exports.fn = () => { - return { - element: { - enter: (node) => { - if (node.name === 'defs') { - /** - * @type {Map<string, number>} - */ - const frequencies = new Map(); - for (const child of node.children) { - if (child.type === 'element') { - const frequency = frequencies.get(child.name); - if (frequency == null) { - frequencies.set(child.name, 1); - } else { - frequencies.set(child.name, frequency + 1); - } - } - } - node.children.sort((a, b) => { - if (a.type !== 'element' || b.type !== 'element') { - return 0; - } - const aFrequency = frequencies.get(a.name); - const bFrequency = frequencies.get(b.name); - if (aFrequency != null && bFrequency != null) { - const frequencyComparison = bFrequency - aFrequency; - if (frequencyComparison !== 0) { - return frequencyComparison; - } - } - const lengthComparison = b.name.length - a.name.length; - if (lengthComparison !== 0) { - return lengthComparison; - } - if (a.name !== b.name) { - return a.name > b.name ? -1 : 1; - } - return 0; - }); - } - }, - }, - }; -}; |
