1
0
mirror of https://github.com/svg/svgo.git synced 2025-06-30 07:21:50 +03:00
Files
svgo/plugins/mergeStyles.js
2025-05-11 15:38:41 +01:00

82 lines
2.2 KiB
JavaScript

import { detachNodeFromParent } from '../lib/xast.js';
import { visitSkip } from '../lib/util/visit.js';
export const name = 'mergeStyles';
export const description = 'merge multiple style elements into one';
/**
* Merge multiple style elements into one.
*
* @author strarsis <strarsis@gmail.com>
*
* @type {import('../lib/types.js').Plugin}
*/
export const fn = () => {
/** @type {?import('../lib/types.js').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 {import('../lib/types.js').XastChild} */
const child = { type: styleContentType, value: collectedStyles };
firstStyleElement.children = [child];
}
},
},
};
};