Repository: svg/svgo
Pull request #610 (2016-10-08 19:57:57)

Allow to force clean svg with style or script, excepting custom ids (in cleanupIDs)
When processing svg files that make use of [svg-stacks](http://xn--dahlstrm-t4a.net/svg/structure/svgstack-usage.html) or [view elements](https://betravis.github.io/icon-methods/svg-sprite-sheets#with-a-custom-view) cleanupIDs plugin would either leave the file untouched (when `<style>` is present) or delete all ids from `<view>` elements.

I have added **force** (false by default) parameter to **cleanupIDs** config that allows to ignore the presence of `<style>` and `<script>` and clean up anyway. In case of **force = true** the onus would be on the user not to reference elements by id in those tags. Or to include ids not to be cleaned in **except** parameter.

**except**, which can be a single id or an Array of ids, allows to keep user-specified ids from being removed or minified. This would make possible referencing them externally as part of `<view id="#id">` or an **svg-stack**. For example,

`<img src="image.svg#figure">`

**except**ing ids in `<symbol>` would also allow to use **cleanupIDs** with such svg files (still need to disable **removeUselessDefs** though).



Patch:
diff --git a/plugins/cleanupIDs.js b/plugins/cleanupIDs.js
index 38b5cfb..2546bc0 100644
--- a/plugins/cleanupIDs.js
+++ b/plugins/cleanupIDs.js
@@ -9,7 +9,9 @@ exports.description = 'removes unused IDs and minifies used';
 exports.params = {
     remove: true,
     minify: true,
-    prefix: ''
+    prefix: '',
+    force: false,
+    except: []
 };
 
 var referencesProps = require('./_collections').referencesProps,
@@ -39,7 +41,8 @@ exports.fn = function(data, params) {
         IDs = Object.create(null),
         referencesIDs = Object.create(null),
         idPrefix = 'id-', // prefix IDs so that values like '__proto__' don't break the work
-        hasStyleOrScript = false;
+        hasStyleOrScript = false,
+        exceptIDs = Array.isArray(params.except) ? params.except : [params.except];
 
     /**
      * Bananas!
@@ -55,7 +58,7 @@ exports.fn = function(data, params) {
                 match;
 
             // check if <style> of <script> presents
-            if (item.isElem(styleOrScript)) {
+            if (!params.force && item.isElem(styleOrScript)) {
                 hasStyleOrScript = true;
                 continue;
             }
@@ -127,7 +130,7 @@ exports.fn = function(data, params) {
             idKey = k;
             k = k.replace(idPrefix, '');
             // replace referenced IDs with the minified ones
-            if (params.minify) {
+            if (params.minify && exceptIDs.indexOf(k) === -1) {
                 currentIDstring = getIDstring(currentID = generateID(currentID), params);
                 IDs[idKey].attr('id').value = currentIDstring;
 
@@ -149,7 +152,9 @@ exports.fn = function(data, params) {
     if (params.remove) {
 
         for(var ID in IDs) {
-            IDs[ID].removeAttr('id');
+            if (exceptIDs.indexOf(ID.replace(idPrefix, '')) === -1) {
+                IDs[ID].removeAttr('id');
+            }
         }
 
     }


Test patch:
diff --git a/test/plugins/cleanupIDs.06.svg b/test/plugins/cleanupIDs.06.svg
new file mode 100644
index 0000000..bf0c948
--- /dev/null
+++ b/test/plugins/cleanupIDs.06.svg
@@ -0,0 +1,19 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <style>
+        …
+    </style>
+    <circle id="circle001" fill="red" cx="60" cy="60" r="50"/>
+</svg>
+
+@@@
+
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <style>
+        …
+    </style>
+    <circle fill="red" cx="60" cy="60" r="50"/>
+</svg>
+
+@@@
+
+{"force": true}
diff --git a/test/plugins/cleanupIDs.07.svg b/test/plugins/cleanupIDs.07.svg
new file mode 100644
index 0000000..320a8dc
--- /dev/null
+++ b/test/plugins/cleanupIDs.07.svg
@@ -0,0 +1,19 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <script>
+        …
+    </script>
+    <circle id="circle001" fill="red" cx="60" cy="60" r="50"/>
+</svg>
+
+@@@
+
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <script>
+        …
+    </script>
+    <circle fill="red" cx="60" cy="60" r="50"/>
+</svg>
+
+@@@
+
+{"force": true}
diff --git a/test/plugins/cleanupIDs.08.svg b/test/plugins/cleanupIDs.08.svg
new file mode 100644
index 0000000..1d0527b
--- /dev/null
+++ b/test/plugins/cleanupIDs.08.svg
@@ -0,0 +1,19 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 230 120">
+    <circle id="circle001" fill="red" cx="60" cy="60" r="50"/>
+    <rect id="rect001" fill="blue" x="120" y="10" width="100" height="100"/>
+    <view id="circle" viewBox="0 0 120 120"/>
+    <view id="rect" viewBox="110 0 120 120"/>
+</svg>
+
+@@@
+
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 230 120">
+    <circle fill="red" cx="60" cy="60" r="50"/>
+    <rect fill="blue" x="120" y="10" width="100" height="100"/>
+    <view id="circle" viewBox="0 0 120 120"/>
+    <view id="rect" viewBox="110 0 120 120"/>
+</svg>
+
+@@@
+
+{"except": ["circle", "rect"]}
diff --git a/test/plugins/cleanupIDs.09.svg b/test/plugins/cleanupIDs.09.svg
new file mode 100644
index 0000000..4878bab
--- /dev/null
+++ b/test/plugins/cleanupIDs.09.svg
@@ -0,0 +1,22 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 120 120">
+    <style>
+        svg .hidden { display: none; }
+        svg .hidden:target { display: inline; }
+    </style>
+    <circle id="circle" class="hidden" fill="red" cx="60" cy="60" r="50"/>
+    <rect id="rect" class="hidden" fill="blue" x="10" y="10" width="100" height="100"/>
+</svg>
+
+@@@
+
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 120 120">
+    <style>
+        svg .hidden { display: none; } svg .hidden:target { display: inline; }
+    </style>
+    <circle id="circle" class="hidden" fill="red" cx="60" cy="60" r="50"/>
+    <rect id="rect" class="hidden" fill="blue" x="10" y="10" width="100" height="100"/>
+</svg>
+
+@@@
+
+{"force": true, "except": ["circle", "rect"]}
diff --git a/test/plugins/cleanupIDs.10.svg b/test/plugins/cleanupIDs.10.svg
new file mode 100644
index 0000000..11b0671
--- /dev/null
+++ b/test/plugins/cleanupIDs.10.svg
@@ -0,0 +1,34 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 120 120">
+    <style>
+        svg .hidden { display: none; }
+        svg .hidden:target { display: inline; }
+    </style>
+    <defs>
+        <circle id="circle" fill="red" cx="60" cy="60" r="50"/>
+        <rect id="rect" fill="blue" x="10" y="10" width="100" height="100"/>
+    </defs>
+    <g id="figure" class="hidden">
+        <use xlink:href="#circle"/>
+        <use xlink:href="#rect"/>
+    </g>
+</svg>
+
+@@@
+
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 120 120">
+    <style>
+        svg .hidden { display: none; } svg .hidden:target { display: inline; }
+    </style>
+    <defs>
+        <circle id="a" fill="red" cx="60" cy="60" r="50"/>
+        <rect id="b" fill="blue" x="10" y="10" width="100" height="100"/>
+    </defs>
+    <g id="figure" class="hidden">
+        <use xlink:href="#a"/>
+        <use xlink:href="#b"/>
+    </g>
+</svg>
+
+@@@
+
+{"force": true, "except": "figure"}