mirror of
https://github.com/svg/svgo.git
synced 2026-01-27 07:02:06 +03:00
MDN used to redirect hits to `/docs` to the users locale, however they stopped doing that for some reason. This broke all MDN links on our documentation/JSDocs. This inserts `en-US` to all MDN URLs which fixes the links. (Unfortunately, this means all users may be taken to the English site regardless of their language preference.)
158 lines
4.5 KiB
JavaScript
158 lines
4.5 KiB
JavaScript
import { attrsGroupsDefaults, colorsProps } from './_collections.js';
|
|
import {
|
|
detachNodeFromParent,
|
|
querySelector,
|
|
querySelectorAll,
|
|
} from '../lib/xast.js';
|
|
import { collectStylesheet, computeStyle } from '../lib/style.js';
|
|
|
|
export const name = 'convertOneStopGradients';
|
|
export const 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('../lib/types.js').Plugin}
|
|
* @see https://developer.mozilla.org/en-US/docs/Web/SVG/Element/linearGradient
|
|
* @see https://developer.mozilla.org/en-US/docs/Web/SVG/Element/radialGradient
|
|
*/
|
|
export const fn = (root) => {
|
|
const stylesheet = collectStylesheet(root);
|
|
|
|
/**
|
|
* Parent defs that had gradients elements removed from them.
|
|
*
|
|
* @type {Set<import('../lib/types.js').XastElement>}
|
|
*/
|
|
const effectedDefs = new Set();
|
|
|
|
/** @type {Map<import('../lib/types.js').XastElement, import('../lib/types.js').XastParent>} */
|
|
const allDefs = new Map();
|
|
|
|
/** @type {Map<import('../lib/types.js').XastElement, import('../lib/types.js').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'];
|
|
const 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);
|
|
}
|
|
}
|
|
}
|
|
},
|
|
},
|
|
};
|
|
};
|