Skip to content

Commit 26e86e5

Browse files
authored
fix: remove unused <use> elements when deleting empty symbols (#2051)
1 parent 50c326b commit 26e86e5

File tree

2 files changed

+72
-0
lines changed

2 files changed

+72
-0
lines changed

plugins/removeEmptyContainers.js

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { elemsGroups } from './_collections.js';
22
import { detachNodeFromParent } from '../lib/xast.js';
33
import { collectStylesheet, computeStyle } from '../lib/style.js';
4+
import { findReferences } from '../lib/svgo/tools.js';
45

56
export const name = 'removeEmptyContainers';
67
export const description = 'removes empty container elements';
@@ -22,9 +23,33 @@ export const description = 'removes empty container elements';
2223
*/
2324
export const fn = (root) => {
2425
const stylesheet = collectStylesheet(root);
26+
const removedIds = new Set();
27+
/**
28+
* @type {Map<string, {
29+
* node: import('../lib/types.js').XastElement,
30+
* parent: import('../lib/types.js').XastParent,
31+
* }[]>}
32+
*/
33+
const usesById = new Map();
2534

2635
return {
2736
element: {
37+
enter: (node, parentNode) => {
38+
if (node.name === 'use') {
39+
// Record uses so those referencing empty containers can be removed.
40+
for (const [name, value] of Object.entries(node.attributes)) {
41+
const ids = findReferences(name, value);
42+
for (const id of ids) {
43+
let references = usesById.get(id);
44+
if (references === undefined) {
45+
references = [];
46+
usesById.set(id, references);
47+
}
48+
references.push({ node: node, parent: parentNode });
49+
}
50+
}
51+
}
52+
},
2853
exit: (node, parentNode) => {
2954
// remove only empty non-svg containers
3055
if (
@@ -61,6 +86,22 @@ export const fn = (root) => {
6186
}
6287

6388
detachNodeFromParent(node, parentNode);
89+
if (node.attributes.id) {
90+
removedIds.add(node.attributes.id);
91+
}
92+
},
93+
},
94+
root: {
95+
exit: () => {
96+
// Remove any <use> elements that referenced an empty container.
97+
for (const id of removedIds) {
98+
const uses = usesById.get(id);
99+
if (uses) {
100+
for (const use of uses) {
101+
detachNodeFromParent(use.node, use.parent);
102+
}
103+
}
104+
}
64105
},
65106
},
66107
};
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
If a container with an id attribute is removed, remove any <use>s associated with the id.
2+
3+
===
4+
5+
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 100 100">
6+
<symbol id="a">
7+
<path d="M 10 10 H 90" style="stroke:black;stroke-width:2"/>
8+
</symbol>
9+
<symbol id="b">
10+
<path d="M 10 20 H 90"/>
11+
</symbol>
12+
<symbol id="c"/>
13+
<symbol id="d"/>
14+
<use xlink:href="#a"/>
15+
<use href="#b" style="stroke:red;stroke-width:2"/>
16+
<use xlink:href="#c"/>
17+
<use href="#d" style="stroke:red;stroke-width:2"/>
18+
</svg>
19+
20+
@@@
21+
22+
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 100 100">
23+
<symbol id="a">
24+
<path d="M 10 10 H 90" style="stroke:black;stroke-width:2"/>
25+
</symbol>
26+
<symbol id="b">
27+
<path d="M 10 20 H 90"/>
28+
</symbol>
29+
<use xlink:href="#a"/>
30+
<use href="#b" style="stroke:red;stroke-width:2"/>
31+
</svg>

0 commit comments

Comments
 (0)