Skip to content

Commit 2d9e101

Browse files
authored
fix(removeHiddenElems): handle defs better (#1879)
1 parent 3dc2f6f commit 2d9e101

File tree

3 files changed

+97
-25
lines changed

3 files changed

+97
-25
lines changed

plugins/removeHiddenElems.js

Lines changed: 63 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ const {
1515
} = require('../lib/xast.js');
1616
const { collectStylesheet, computeStyle } = require('../lib/style.js');
1717
const { parsePathData } = require('../lib/path.js');
18+
const { hasScripts } = require('../lib/svgo/tools.js');
1819

1920
const nonRendering = elemsGroups.nonRendering;
2021

@@ -74,6 +75,21 @@ exports.fn = (root, params) => {
7475
*/
7576
const removedDefIds = new Set();
7677

78+
/**
79+
* @type {Map<XastElement, XastParent>}
80+
*/
81+
const allDefs = new Map();
82+
83+
/**
84+
* @type {Map<string, Array<{ node: XastElement, parentNode: XastParent }>>}
85+
*/
86+
const referencesById = new Map();
87+
88+
/**
89+
* If styles are present, we can't be sure if a definition is unused or not
90+
*/
91+
let deoptimized = false;
92+
7793
/**
7894
* @param {XastChild} node
7995
* @param {XastParent} parentNode
@@ -123,6 +139,33 @@ exports.fn = (root, params) => {
123139
return {
124140
element: {
125141
enter: (node, parentNode) => {
142+
if (
143+
(node.name === 'style' && node.children.length !== 0) ||
144+
hasScripts(node)
145+
) {
146+
deoptimized = true;
147+
return;
148+
}
149+
150+
if (node.name === 'defs') {
151+
allDefs.set(node, parentNode);
152+
}
153+
154+
if (node.name === 'use') {
155+
for (const attr of Object.keys(node.attributes)) {
156+
if (attr !== 'href' && !attr.endsWith(':href')) continue;
157+
const value = node.attributes[attr];
158+
const id = value.slice(1);
159+
160+
let refs = referencesById.get(id);
161+
if (!refs) {
162+
refs = [];
163+
referencesById.set(id, refs);
164+
}
165+
refs.push({ node, parentNode });
166+
}
167+
}
168+
126169
// Removes hidden elements
127170
// https://www.w3schools.com/cssref/pr_class_visibility.asp
128171
const computedStyle = computeStyle(stylesheet, node);
@@ -350,46 +393,41 @@ exports.fn = (root, params) => {
350393
removeElement(node, parentNode);
351394
}
352395
},
353-
354-
exit: (node, parentNode) => {
355-
if (node.name === 'defs' && node.children.length === 0) {
356-
removeElement(node, parentNode);
357-
return;
358-
}
359-
360-
if (node.name === 'use') {
361-
const referencesRemovedDef = Object.entries(node.attributes).some(
362-
([attrKey, attrValue]) =>
363-
(attrKey === 'href' || attrKey.endsWith(':href')) &&
364-
removedDefIds.has(
365-
attrValue.slice(attrValue.indexOf('#') + 1).trim()
366-
)
367-
);
368-
369-
if (referencesRemovedDef) {
370-
detachNodeFromParent(node, parentNode);
396+
},
397+
root: {
398+
exit: () => {
399+
for (const id of removedDefIds) {
400+
const refs = referencesById.get(id);
401+
if (refs) {
402+
for (const { node, parentNode } of refs) {
403+
detachNodeFromParent(node, parentNode);
404+
}
371405
}
372-
373-
return;
374406
}
375407

376-
if (node.name === 'svg' && parentNode.type === 'root') {
408+
if (!deoptimized) {
377409
for (const [
378410
nonRenderedNode,
379411
nonRenderedParent,
380412
] of nonRenderedNodes.entries()) {
413+
const id = nonRenderedNode.attributes.id;
381414
const selector = referencesProps
382-
.map(
383-
(attr) => `[${attr}="url(#${nonRenderedNode.attributes.id})"]`
384-
)
415+
.map((attr) => `[${attr}="url(#${id})"]`)
416+
.concat(`[href="#${id}"]`, `[xlink\\:href="#${id}"]`)
385417
.join(',');
386418

387419
const element = querySelector(root, selector);
388420
if (element == null) {
389-
detachNodeFromParent(node, nonRenderedParent);
421+
detachNodeFromParent(nonRenderedNode, nonRenderedParent);
390422
}
391423
}
392424
}
425+
426+
for (const [node, parentNode] of allDefs.entries()) {
427+
if (node.children.length === 0) {
428+
detachNodeFromParent(node, parentNode);
429+
}
430+
}
393431
},
394432
},
395433
};
Lines changed: 14 additions & 0 deletions
Loading
Lines changed: 20 additions & 0 deletions
Loading

0 commit comments

Comments
 (0)