1
0
mirror of https://github.com/svg/svgo.git synced 2025-04-19 10:22:15 +03:00
svgo/plugins/removeEmptyContainers.js
Seth Falco f3495ff6c9
fix(removeEmptyContainers): skip if filter is applied via styles as well (#2089)
Fixes a bug where we were too eager to remove empty containers.

We already had logic to skip removing empty containers if it had the
filter attribute, which is needed to apply a filter to the whole area.
However, the filter can also be defined through CSS. We did not
properly handle this case, and treated the node as if it had no filter
at all.

This computes the styles and checks the stylesheet as well. (We also
move the logic down to avoid computing the styles eagerly.)
2024-12-22 16:01:22 +00:00

68 lines
1.7 KiB
JavaScript

import { elemsGroups } from './_collections.js';
import { detachNodeFromParent } from '../lib/xast.js';
import { collectStylesheet, computeStyle } from '../lib/style.js';
export const name = 'removeEmptyContainers';
export const 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.js').Plugin<'removeEmptyContainers'>}
*/
export const fn = (root) => {
const stylesheet = collectStylesheet(root);
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;
}
// empty <mask> hides masked element
if (node.name === 'mask' && node.attributes.id != null) {
return;
}
if (parentNode.type === 'element' && parentNode.name === 'switch') {
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 ||
computeStyle(stylesheet, node).filter)
) {
return;
}
detachNodeFromParent(node, parentNode);
},
},
};
};