diff options
Diffstat (limited to 'node_modules/svgo/plugins/mergePaths.js')
| -rw-r--r-- | node_modules/svgo/plugins/mergePaths.js | 108 |
1 files changed, 79 insertions, 29 deletions
diff --git a/node_modules/svgo/plugins/mergePaths.js b/node_modules/svgo/plugins/mergePaths.js index 05c7ac70..fe982c13 100644 --- a/node_modules/svgo/plugins/mergePaths.js +++ b/node_modules/svgo/plugins/mergePaths.js @@ -1,6 +1,11 @@ 'use strict'; -const { detachNodeFromParent } = require('../lib/xast.js'); +/** + * @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'); @@ -25,73 +30,118 @@ exports.fn = (root, params) => { return { element: { enter: (node) => { - let prevChild = null; + 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]; - for (const child of node.children) { - // skip if previous element is not path or contains animation elements if ( - prevChild == null || 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; } - // skip if element is not path or contains animation elements if ( child.type !== 'element' || child.name !== 'path' || child.children.length !== 0 || child.attributes.d == null ) { + if (prevPathData) { + updatePreviousPath(prevChild, prevPathData); + } prevChild = child; continue; } - // preserve paths with markers const computedStyle = computeStyle(stylesheet, child); if ( computedStyle['marker-start'] || computedStyle['marker-mid'] || computedStyle['marker-end'] ) { + if (prevPathData) { + updatePreviousPath(prevChild, prevPathData); + } prevChild = child; continue; } - - const prevChildAttrs = Object.keys(prevChild.attributes); const childAttrs = Object.keys(child.attributes); - let attributesAreEqual = prevChildAttrs.length === childAttrs.length; - for (const name of childAttrs) { - if (name !== 'd') { - if ( - prevChild.attributes[name] == null || - prevChild.attributes[name] !== child.attributes[name] - ) { - attributesAreEqual = false; - } + if (childAttrs.length !== Object.keys(prevChild.attributes).length) { + if (prevPathData) { + updatePreviousPath(prevChild, prevPathData); } + prevChild = child; + continue; } - const prevPathJS = path2js(prevChild); - const curPathJS = path2js(child); - if ( - attributesAreEqual && - (force || !intersects(prevPathJS, curPathJS)) - ) { - js2path(prevChild, prevPathJS.concat(curPathJS), { - floatPrecision, - noSpaceAfterFlags, - }); - detachNodeFromParent(child, node); + 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), + ); }, }, }; |
