Skip to content

Commit

Permalink
Get rid of call to detachNodeFromParent().
Browse files Browse the repository at this point in the history
  • Loading branch information
johnkenny54 committed Nov 11, 2024
1 parent fc83e00 commit dff0f3a
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 27 deletions.
23 changes: 23 additions & 0 deletions lib/svgo/tools.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,19 @@ export const decodeSVGDatauri = (str) => {
return str;
};

/**
* @param {Map<import('../types.js').XastParent,Set<import('../types.js').XastChild>>} childrenToDeleteByParent
* @param {import('../types.js').XastChild} child
*/
export function addChildToDelete(childrenToDeleteByParent, child) {
let childrenToDelete = childrenToDeleteByParent.get(child.parentNode);
if (!childrenToDelete) {
childrenToDelete = new Set();
childrenToDeleteByParent.set(child.parentNode, childrenToDelete);
}
childrenToDelete.add(child);
}

/**
* @typedef {{
* noSpaceAfterFlags?: boolean,
Expand Down Expand Up @@ -124,6 +137,16 @@ export const cleanupOutData = (data, params, command) => {
return str;
};

/**
* @param {Map<import('../types.js').XastParent,Set<import('../types.js').XastChild>>} childrenToDeleteByParent
*/
export function deleteChildren(childrenToDeleteByParent) {
// For each parent, delete no longer needed children.
for (const [parent, childrenToDelete] of childrenToDeleteByParent) {
parent.children = parent.children.filter((c) => !childrenToDelete.has(c));
}
}

/**
* @param {number} n
* @param {number} m
Expand Down
39 changes: 25 additions & 14 deletions plugins/removeEmptyContainers.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import { getHrefId } from '../lib/svgo/tools.js';
import {
addChildToDelete,
deleteChildren,
getHrefId,
} from '../lib/svgo/tools.js';
import { detachNodeFromParent } from '../lib/xast.js';

export const name = 'removeEmptyContainers';
Expand Down Expand Up @@ -28,9 +32,7 @@ const removableEls = new Set([
*/
export const fn = () => {
const removedIds = new Set();
/**
* @type {Map<string, import('../lib/types.js').XastElement[]>}
*/
/** @type {Map<string, import('../lib/types.js').XastElement[]>} */
const usesById = new Map();

return {
Expand All @@ -49,48 +51,57 @@ export const fn = () => {
}
}
},
exit: (node, parentNode) => {
exit: (element, parentNode) => {
// remove only empty non-svg containers
if (!removableEls.has(node.name) || node.children.length !== 0) {
if (!removableEls.has(element.name) || element.children.length !== 0) {
return;
}
// empty patterns may contain reusable configuration
if (
node.name === 'pattern' &&
Object.keys(node.attributes).length !== 0
element.name === 'pattern' &&
Object.keys(element.attributes).length !== 0
) {
return;
}
// The <g> may not have content, but the filter may cause a rectangle
// to be created and filled with pattern.
if (node.name === 'g' && node.attributes.filter != null) {
if (element.name === 'g' && element.attributes.filter != null) {
return;
}
// empty <mask> hides masked element
if (node.name === 'mask' && node.attributes.id != null) {
if (element.name === 'mask' && element.attributes.id != null) {
return;
}
if (parentNode.type === 'element' && parentNode.name === 'switch') {
return;
}

detachNodeFromParent(node, parentNode);
if (node.attributes.id) {
removedIds.add(node.attributes.id);
// TODO: Change the way this works so that parent removes empty children. We can't queue them for deletion in
// root exit; this is running in element exit so that nested empty elements are removed from bottom up, the nesting
// would be hard to detect in root exit.
detachNodeFromParent(element, parentNode);
if (element.attributes.id) {
removedIds.add(element.attributes.id);
}
},
},
root: {
exit: () => {
// Remove any <use> elements that referenced an empty container.

/** @type {Map<import('../lib/types.js').XastParent,Set<import('../lib/types.js').XastChild>>} */
const childrenToDelete = new Map();

for (const id of removedIds) {
const usingEls = usesById.get(id);
if (usingEls) {
for (const element of usingEls) {
detachNodeFromParent(element);
addChildToDelete(childrenToDelete, element);
}
}
}

deleteChildren(childrenToDelete);
},
},
};
Expand Down
20 changes: 7 additions & 13 deletions plugins/removeHiddenElems.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import { parsePathCommands, PathParseError } from '../lib/pathutils.js';
import { getEllipseProperties } from '../lib/svgo/tools.js';
import {
addChildToDelete,
deleteChildren,
getEllipseProperties,
} from '../lib/svgo/tools.js';
import { elemsGroups } from './_collections.js';

export const name = 'removeHiddenElems';
Expand Down Expand Up @@ -44,12 +48,7 @@ export const fn = (root, params, info) => {
* @param {import('../lib/types.js').XastElement} element
*/
function removeElement(element) {
let childrenToDelete = childrenToDeleteByParent.get(element.parentNode);
if (!childrenToDelete) {
childrenToDelete = new Set();
childrenToDeleteByParent.set(element.parentNode, childrenToDelete);
}
childrenToDelete.add(element);
addChildToDelete(childrenToDeleteByParent, element);
}

/**
Expand Down Expand Up @@ -200,12 +199,7 @@ export const fn = (root, params, info) => {
},
root: {
exit: () => {
// For each parent, delete no longer needed children.
for (const [parent, childrenToDelete] of childrenToDeleteByParent) {
parent.children = parent.children.filter(
(c) => !childrenToDelete.has(c),
);
}
deleteChildren(childrenToDeleteByParent);
},
},
};
Expand Down

0 comments on commit dff0f3a

Please sign in to comment.