Skip to content

Commit

Permalink
fix(cleanupEnableBackground): clean up inline styles too (#1866)
Browse files Browse the repository at this point in the history
  • Loading branch information
SethFalco authored Dec 2, 2023
1 parent 39e855b commit a663d8d
Show file tree
Hide file tree
Showing 3 changed files with 146 additions and 30 deletions.
12 changes: 10 additions & 2 deletions docs/03-plugins/cleanup-enable-background.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,19 @@ svgo:
defaultPlugin: true
---

Removes the [`enable-background`](https://developer.mozilla.org/docs/Web/SVG/Attribute/enable-background) attribute, unless it's used in a [`<filter>`](https://developer.mozilla.org/docs/Web/SVG/Element/filter).
Cleans up [`enable-background`](https://developer.mozilla.org/docs/Web/SVG/Attribute/enable-background), unless the document uses [`<filter>`](https://developer.mozilla.org/docs/Web/SVG/Element/filter) elements.

Only cleans up attribute values and inline-styles, but does not effect stylesheets in [`<style>`](https://developer.mozilla.org/docs/Web/SVG/Element/style) nodes.

This plugin will:

* Drop `enable-background` when the width and height values match the width and height of the `<svg>` node.
* Replace the value with `new` when the width and height values match the width and height of a `<mask>` or `<pattern>` node.


:::info

Some browsers don't support `enable-background`, so it's best to avoid that attribute regardless.
Some browsers don't support `enable-background`, so it's best to avoid the attribute regardless.

:::

Expand Down
148 changes: 120 additions & 28 deletions plugins/cleanupEnableBackground.js
Original file line number Diff line number Diff line change
@@ -1,30 +1,29 @@
'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) => {
const regEnableBackground =
/^new\s0\s0\s([-+]?\d*\.?\d+([eE][-+]?\d+)?)\s([-+]?\d*\.?\d+([eE][-+]?\d+)?)$/;

let hasFilter = false;

visit(root, {
element: {
enter: (node) => {
Expand All @@ -38,36 +37,129 @@ exports.fn = (root) => {
return {
element: {
enter: (node) => {
if (node.attributes['enable-background'] == null) {
/** @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;
}
if (hasFilter) {

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 (
(node.name === 'svg' ||
node.name === 'mask' ||
node.name === 'pattern') &&
node.attributes.width != null &&
node.attributes.height != null
newStyle?.type === 'DeclarationList' &&
enableBackgroundDeclaration
) {
const match =
node.attributes['enable-background'].match(regEnableBackground);
if (
match != null &&
node.attributes.width === match[1] &&
node.attributes.height === match[3]
) {
if (node.name === 'svg') {
delete node.attributes['enable-background'];
} else {
node.attributes['enable-background'] = 'new';
}
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);
}
}
} else {
//we don't need 'enable-background' if we have no filters
delete node.attributes['enable-background'];
}

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;
};
16 changes: 16 additions & 0 deletions test/plugins/cleanupEnableBackground.05.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit a663d8d

Please sign in to comment.