Repository: svg/svgo
Pull request #375 (2015-06-02 10:56:05)

Allow providing custom plugins
When experimenting with a new plugin, or just doing custom things in my app, it's a pain to have to fork SVGO and add a plugin there then keep the fork up to date. It would be great to just be able to pass a custom plugin into the plugins list.

This pull request adds that functionality, so you can do something like:

```javascript
new SVGO({
    plugins: [
        myCustomPugin: {
            type: ‘perItem’,
            fn:   function(item) {
                /* do stuff */
            }
        }
    ]
});
```


Patch:
diff --git a/lib/svgo/config.js b/lib/svgo/config.js
index ca01dcc..f3c34ef 100644
--- a/lib/svgo/config.js
+++ b/lib/svgo/config.js
@@ -71,23 +71,31 @@ function preparePluginsArray(plugins) {
         if (typeof item === 'object') {
 
             key = Object.keys(item)[0];
-            plugin = EXTEND({}, require('../../plugins/' + key));
 
-            // name: {}
-            if (typeof item[key] === 'object') {
-                plugin.params = EXTEND({}, plugin.params || {}, item[key]);
-                plugin.active = true;
+            // custom
+            if (typeof item[key] === 'object' && item[key].fn && typeof item[key].fn === 'function') {
+                plugin = setupCustomPlugin(key, item[key]);
 
-            // name: false
-            } else if (item[key] === false) {
-               plugin.active = false;
+            } else {
 
-            // name: true
-            } else if (item[key] === true) {
-               plugin.active = true;
-            }
+              plugin = EXTEND({}, require('../../plugins/' + key));
+
+              // name: {}
+              if (typeof item[key] === 'object') {
+                  plugin.params = EXTEND({}, plugin.params || {}, item[key]);
+                  plugin.active = true;
+
+              // name: false
+              } else if (item[key] === false) {
+                 plugin.active = false;
+
+              // name: true
+              } else if (item[key] === true) {
+                 plugin.active = true;
+              }
 
-            plugin.name = key;
+              plugin.name = key;
+            }
 
         // name
         } else {
@@ -124,25 +132,30 @@ function extendConfig(defaults, config) {
 
                 key = Object.keys(item)[0];
 
-                defaults.plugins.forEach(function(plugin) {
+                // custom
+                if (typeof item[key] === 'object' && item[key].fn && typeof item[key].fn === 'function') {
+                    defaults.plugins.push(setupCustomPlugin(key, item[key]));
 
-                    if (plugin.name === key) {
-                        // name: {}
-                        if (typeof item[key] === 'object') {
-                            plugin.params = EXTEND({}, plugin.params || {}, item[key]);
-                            plugin.active = true;
+                } else {
+                    defaults.plugins.forEach(function(plugin) {
 
-                        // name: false
-                        } else if (item[key] === false) {
-                           plugin.active = false;
+                        if (plugin.name === key) {
+                            // name: {}
+                            if (typeof item[key] === 'object') {
+                                plugin.params = EXTEND({}, plugin.params || {}, item[key]);
+                                plugin.active = true;
 
-                        // name: true
-                        } else if (item[key] === true) {
-                           plugin.active = true;
-                        }
-                    }
+                            // name: false
+                            } else if (item[key] === false) {
+                               plugin.active = false;
 
-                });
+                            // name: true
+                            } else if (item[key] === true) {
+                               plugin.active = true;
+                            }
+                        }
+                    });
+                }
 
             }
 
@@ -164,6 +177,21 @@ function extendConfig(defaults, config) {
 
 }
 
+/**
+ * Setup and enable a custom plugin
+ *
+ * @param {String} plugin name
+ * @param {Object} custom plugin
+ * @return {Array} enabled plugin
+ */
+function setupCustomPlugin(name, plugin) {
+    plugin.active = true;
+    plugin.params = EXTEND({}, plugin.params || {});
+    plugin.name = name;
+
+    return plugin;
+}
+
 /**
  * Try to group sequential elements of plugins array.
  *


Test patch:
diff --git a/test/config/_index.js b/test/config/_index.js
index 7f4193b..ae6a584 100644
--- a/test/config/_index.js
+++ b/test/config/_index.js
@@ -103,6 +103,62 @@ describe('config', function() {
 
     });
 
+
+    describe('custom plugins', function() {
+
+        describe('extend config with custom plugin', function() {
+            var config = CONFIG({
+                    plugins: [
+                        {
+                            aCustomPlugin: {
+                                type: 'perItem',
+                                fn: function() { }
+                            }
+                        }
+                    ]
+                }),
+                customPlugin = getPlugin('aCustomPlugin', config.plugins);
+
+            it('custom plugin should be enabled', function() {
+                return customPlugin.active.should.be.true;
+            });
+
+            it('custom plugin should have been given a name', function() {
+                return customPlugin.name.should.equal('aCustomPlugin');
+            });
+        });
+
+        describe('replace default config with custom plugin', function() {
+
+            var config = CONFIG({
+                    full: true,
+                    plugins: [
+                        {
+                            aCustomPlugin: {
+                                type: 'perItem',
+                                fn: function() { }
+                            }
+                        }
+                    ]
+                }),
+                customPlugin = getPlugin('aCustomPlugin', config.plugins);
+
+            it('config.plugins should have length 1', function() {
+                return config.plugins.should.have.length(1);
+            });
+
+            it('custom plugin should be enabled', function() {
+                return customPlugin.active.should.be.true;
+            });
+
+            it('custom plugin should have been given a name', function() {
+                return customPlugin.name.should.equal('aCustomPlugin');
+            });
+
+        });
+
+    });
+
 });
 
 function getPlugin(name, plugins) {