Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor removeUnusedNS plugin #1559

Merged
merged 2 commits into from
Sep 10, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 0 additions & 20 deletions lib/xast.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,26 +55,6 @@ const closestByName = (node, name) => {
};
exports.closestByName = closestByName;

const traverseBreak = Symbol();
exports.traverseBreak = traverseBreak;

/**
* @type {(node: any, fn: any) => any}
*/
const traverse = (node, fn) => {
if (fn(node) === traverseBreak) {
return traverseBreak;
}
if (node.type === 'root' || node.type === 'element') {
for (const child of node.children) {
if (traverse(child, fn) === traverseBreak) {
return traverseBreak;
}
}
}
};
exports.traverse = traverse;

const visitSkip = Symbol();
exports.visitSkip = visitSkip;

Expand Down
109 changes: 44 additions & 65 deletions plugins/removeUnusedNS.js
Original file line number Diff line number Diff line change
@@ -1,82 +1,61 @@
'use strict';

const { traverse } = require('../lib/xast.js');
const { parseName } = require('../lib/svgo/tools.js');

exports.type = 'visitor';
exports.name = 'removeUnusedNS';

exports.type = 'full';

exports.active = true;

exports.description = 'removes unused namespaces declaration';

/**
* Remove unused namespaces declaration.
*
* @param {Object} item current iteration item
* @return {Boolean} if false, item will be filtered out
* Remove unused namespaces declaration from svg element
* which are not used in elements or attributes
*
* @author Kir Belevich
*
* @type {import('../lib/types').Plugin<void>}
*/
exports.fn = function (root) {
let svgElem;
const xmlnsCollection = [];

exports.fn = () => {
/**
* Remove namespace from collection.
*
* @param {String} ns namescape name
* @type {Set<string>}
*/
function removeNSfromCollection(ns) {
const pos = xmlnsCollection.indexOf(ns);

// if found - remove ns from the namespaces collection
if (pos > -1) {
xmlnsCollection.splice(pos, 1);
}
}

traverse(root, (node) => {
if (node.type === 'element') {
if (node.name === 'svg') {
for (const name of Object.keys(node.attributes)) {
const { prefix, local } = parseName(name);
// collect namespaces
if (prefix === 'xmlns' && local) {
xmlnsCollection.push(local);
const unusedNamespaces = new Set();
return {
element: {
enter: (node, parentNode) => {
// collect all namespaces from svg element
// (such as xmlns:xlink="http://www.w3.org/1999/xlink")
if (node.name === 'svg' && parentNode.type === 'root') {
for (const name of Object.keys(node.attributes)) {
if (name.startsWith('xmlns:')) {
const local = name.slice('xmlns:'.length);
unusedNamespaces.add(local);
}
}
}

// if svg element has ns-attr
if (xmlnsCollection.length) {
// save svg element
svgElem = node;
}
}

if (xmlnsCollection.length) {
const { prefix } = parseName(node.name);
// check node for the ns-attrs
if (prefix) {
removeNSfromCollection(prefix);
if (unusedNamespaces.size !== 0) {
// preserve namespace used in nested elements names
if (node.name.includes(':')) {
const [ns] = node.name.split(':');
if (unusedNamespaces.has(ns)) {
unusedNamespaces.delete(ns);
}
}
// preserve namespace used in nested elements attributes
for (const name of Object.keys(node.attributes)) {
if (name.includes(':')) {
const [ns] = name.split(':');
unusedNamespaces.delete(ns);
}
}
}

// check each attr for the ns-attrs
for (const name of Object.keys(node.attributes)) {
const { prefix } = parseName(name);
removeNSfromCollection(prefix);
},
exit: (node, parentNode) => {
// remove unused namespace attributes from svg element
if (node.name === 'svg' && parentNode.type === 'root') {
for (const name of unusedNamespaces) {
delete node.attributes[`xmlns:${name}`];
}
}
}
}
});

// remove svg element ns-attributes if they are not used even once
if (xmlnsCollection.length) {
for (const name of xmlnsCollection) {
delete svgElem.attributes['xmlns:' + name];
}
}

return root;
},
},
};
};
1 change: 0 additions & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
"plugins/removeEditorsNSData.js",
"plugins/removeEmptyAttrs.js",
"plugins/removeNonInheritableGroupAttrs.js",
"plugins/removeUnusedNS.js",
"plugins/removeXMLNS.js",
"plugins/sortAttrs.js",
"plugins/removeEmptyContainers.js",
Expand Down