From 6eb4524aef02a7a59a2d2d26e3155a809d16a1ff Mon Sep 17 00:00:00 2001 From: Bogdan Chadkin Date: Fri, 10 Sep 2021 20:30:34 +0300 Subject: [PATCH] Refactor removeUnusedNS plugin (#1559) - covered with types - migrated to visitor plugin api - dropped traverse utility which is replaced by visitor --- lib/xast.js | 20 ------- plugins/removeUnusedNS.js | 109 +++++++++++++++----------------------- tsconfig.json | 1 - 3 files changed, 44 insertions(+), 86 deletions(-) diff --git a/lib/xast.js b/lib/xast.js index ffa5d4ecc..4b9d49603 100644 --- a/lib/xast.js +++ b/lib/xast.js @@ -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; diff --git a/plugins/removeUnusedNS.js b/plugins/removeUnusedNS.js index 3e956b5f7..800a196a0 100644 --- a/plugins/removeUnusedNS.js +++ b/plugins/removeUnusedNS.js @@ -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} */ -exports.fn = function (root) { - let svgElem; - const xmlnsCollection = []; - +exports.fn = () => { /** - * Remove namespace from collection. - * - * @param {String} ns namescape name + * @type {Set} */ - 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; + }, + }, + }; }; diff --git a/tsconfig.json b/tsconfig.json index 046aacc8c..383037e8b 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -24,7 +24,6 @@ "plugins/removeDimensions.js", "plugins/removeEmptyAttrs.js", "plugins/removeNonInheritableGroupAttrs.js", - "plugins/removeUnusedNS.js", "plugins/removeXMLNS.js", "plugins/sortAttrs.js", "plugins/removeEmptyContainers.js",