diff --git a/lib/svgo.js b/lib/svgo.js index 24a88678..ca364191 100755 --- a/lib/svgo.js +++ b/lib/svgo.js @@ -10,48 +10,52 @@ * @license MIT https://raw.githubusercontent.com/svg/svgo/master/LICENSE */ -var CONFIG = require('./svgo/config.js'), - SVG2JS = require('./svgo/svg2js.js'), - PLUGINS = require('./svgo/plugins.js'), - JSAPI = require('./svgo/jsAPI.js'), - encodeSVGDatauri = require('./svgo/tools.js').encodeSVGDatauri, - JS2SVG = require('./svgo/js2svg.js'); +const { resolvePluginConfig } = require('./svgo/config.js'); +const SVG2JS = require('./svgo/svg2js.js'); +const PLUGINS = require('./svgo/plugins.js'); +const JSAPI = require('./svgo/jsAPI.js'); +const { encodeSVGDatauri } = require('./svgo/tools.js'); +const JS2SVG = require('./svgo/js2svg.js'); -var SVGO = function(config) { - this.config = CONFIG(config); +var SVGO = function(config = {}) { + this.config = config; }; SVGO.prototype.optimize = function(svgstr, info = {}) { - const config = this.config; - if (config.error) { - throw Error(config.error); + const config = this.config; + if (typeof config !== 'object') { + throw Error('Config should be an object') + } + const maxPassCount = config.multipass ? 10 : 1; + let prevResultSize = Number.POSITIVE_INFINITY; + let svgjs = null; + for (let i = 0; i < maxPassCount; i += 1) { + svgjs = SVG2JS(svgstr); + if (svgjs.error == null && config.plugins) { + if (Array.isArray(config.plugins) === false) { + throw Error('Invalid plugins list. Provided \'plugins\' in config should be an array.'); + } + const plugins = config.plugins.map(plugin => resolvePluginConfig(plugin, config)) + svgjs = PLUGINS(svgjs, info, plugins); } - const maxPassCount = config.multipass ? 10 : 1; - let prevResultSize = Number.POSITIVE_INFINITY; - let svgjs = null; - for (let i = 0; i < maxPassCount; i += 1) { - svgjs = SVG2JS(svgstr); - if (svgjs.error == null) { - svgjs = PLUGINS(svgjs, info, config.plugins); - } - svgjs = JS2SVG(svgjs, config.js2svg); - if (svgjs.error) { - throw Error(svgjs.error); - } - info.multipassCount = i; - if (svgjs.data.length < prevResultSize) { - prevResultSize = svgjs.data.length - } else { - if (config.datauri) { - svgjs.data = encodeSVGDatauri(svgjs.data, config.datauri); - } - if (info && info.path) { - svgjs.path = info.path; - } - return svgjs; - } + svgjs = JS2SVG(svgjs, config.js2svg); + if (svgjs.error) { + throw Error(svgjs.error); } - return svgjs; + info.multipassCount = i; + if (svgjs.data.length < prevResultSize) { + prevResultSize = svgjs.data.length + } else { + if (config.datauri) { + svgjs.data = encodeSVGDatauri(svgjs.data, config.datauri); + } + if (info && info.path) { + svgjs.path = info.path; + } + return svgjs; + } + } + return svgjs; }; /** @@ -64,8 +68,6 @@ SVGO.prototype.createContentItem = function(data) { return new JSAPI(data); }; -SVGO.Config = CONFIG; - module.exports = SVGO; // Offer ES module interop compatibility. module.exports.default = SVGO; diff --git a/lib/svgo/config.js b/lib/svgo/config.js index 2876f617..d0d5b3ff 100644 --- a/lib/svgo/config.js +++ b/lib/svgo/config.js @@ -2,52 +2,11 @@ const pluginsMap = require('../../plugins/plugins.js'); -/** - * Read and/or extend/replace default config file, - * prepare and optimize plugins array. - * - * @param {Object} [config] input config - * @return {Object} output config - */ -module.exports = function(config) { - - var defaults; - config = typeof config == 'object' && config || {}; - - if (config.plugins && !Array.isArray(config.plugins)) { - return { error: 'Error: Invalid plugins list. Provided \'plugins\' in config should be an array.' }; - } - - defaults = config; - - if (Array.isArray(defaults.plugins)) { - defaults.plugins = defaults.plugins.map(resolvePluginConfig); - } - - if ('floatPrecision' in config && Array.isArray(defaults.plugins)) { - defaults.plugins.forEach(function(plugin) { - if (plugin.params && ('floatPrecision' in plugin.params)) { - // Don't touch default plugin params - plugin.params = Object.assign({}, plugin.params, { floatPrecision: config.floatPrecision }); - } - }); - } - - if ('datauri' in config) { - defaults.datauri = config.datauri; - } - - if (Array.isArray(defaults.plugins)) { - defaults.plugins = optimizePluginsArray(defaults.plugins); - } else { - defaults.plugins = []; - } - - return defaults; - -}; - -const resolvePluginConfig = plugin => { +const resolvePluginConfig = (plugin, config) => { + let configParams = {}; + if ('floatPrecision' in config) { + configParams.floatPrecision = config.floatPrecision; + } if (typeof plugin === 'string') { // resolve builtin plugin specified as string const pluginConfig = pluginsMap[plugin]; @@ -58,7 +17,7 @@ const resolvePluginConfig = plugin => { ...pluginConfig, name: plugin, active: true, - params: { ...pluginConfig.params } + params: { ...pluginConfig.params, ...configParams } }; } if (typeof plugin === 'object' && plugin != null) { @@ -70,7 +29,7 @@ const resolvePluginConfig = plugin => { return { active: true, ...plugin, - params: { ...plugin.params } + params: { configParams, ...plugin.params } }; } else { // resolve builtin plugin specified as object without implementation @@ -82,30 +41,10 @@ const resolvePluginConfig = plugin => { ...pluginConfig, active: true, ...plugin, - params: { ...pluginConfig.params, ...plugin.params } + params: { ...pluginConfig.params, ...configParams, ...plugin.params } }; } } return null; }; - -/** - * Try to group sequential elements of plugins array. - * - * @param {Object} plugins input plugins - * @return {Array} output plugins - */ -function optimizePluginsArray(plugins) { - - var prev; - - return plugins.reduce(function(plugins, item) { - if (prev && item.type == prev[0].type) { - prev.push(item); - } else { - plugins.push(prev = [item]); - } - return plugins; - }, []); - -} +exports.resolvePluginConfig = resolvePluginConfig; diff --git a/lib/svgo/plugins.js b/lib/svgo/plugins.js index a317f5b6..21a85028 100644 --- a/lib/svgo/plugins.js +++ b/lib/svgo/plugins.js @@ -11,25 +11,33 @@ * @return {Object} output data */ module.exports = function(data, info, plugins) { - - plugins.forEach(function(group) { - - switch(group[0].type) { - case 'perItem': - data = perItem(data, info, group); - break; - case 'perItemReverse': - data = perItem(data, info, group, true); - break; - case 'full': - data = full(data, info, group); - break; - } - - }); - - return data; - + const perItemPlugins = []; + const perItemReversePlugins = []; + const fullPlugins = []; + // Try to group sequential elements of plugins array + for (const plugin of plugins) { + switch(plugin.type) { + case 'perItem': + perItemPlugins.push(plugin); + break; + case 'perItemReverse': + perItemReversePlugins.push(plugin); + break; + case 'full': + fullPlugins.push(plugin); + break; + } + } + if (perItemPlugins.length !== 0) { + data = perItem(data, info, perItemPlugins); + } + if (perItemReversePlugins.length !== 0) { + data = perItem(data, info, perItemReversePlugins, true); + } + if (fullPlugins.length !== 0) { + data = full(data, info, fullPlugins); + } + return data; }; /** diff --git a/plugins/plugins.js b/plugins/plugins.js index 20c03508..a64a0384 100644 --- a/plugins/plugins.js +++ b/plugins/plugins.js @@ -1,3 +1,5 @@ +'use strict'; + exports.addAttributesToSVGElement = require('./addAttributesToSVGElement.js'); exports.addClassesToSVGElement = require('./addClassesToSVGElement.js'); exports.cleanupAttrs = require('./cleanupAttrs.js'); diff --git a/test/config/_index.js b/test/config/_index.js index b76c206c..136b3bd6 100644 --- a/test/config/_index.js +++ b/test/config/_index.js @@ -1,46 +1,20 @@ 'use strict'; const { expect } = require('chai'); - -var CONFIG = require('../../lib/svgo/config'); +const { resolvePluginConfig } = require('../../lib/svgo/config.js'); describe('config', function() { - describe('default config', function() { - - var config = CONFIG(); - - it('should be an instance of Object', function() { - expect(config).to.be.an.instanceOf(Object); - }); - - it('should have property "plugins"', function() { - expect(config).to.have.property('plugins'); - }); - - it('"plugins" should be an instance of Array', function() { - expect(config.plugins).to.be.an.instanceOf(Array); - }); - - }); - describe('extend config with object', function() { - var config = CONFIG({ - multipass: true, - plugins: [ - { name: 'removeDoctype', active: false }, - { name: 'convertColors', params: { shorthex: false } }, - { name: 'removeRasterImages', params: { param: true } } - ] - }), - removeDoctype = getPlugin('removeDoctype', config.plugins), - convertColors = getPlugin('convertColors', config.plugins), - removeRasterImages = getPlugin('removeRasterImages', config.plugins); - - it('should have "multipass"', function() { - expect(config.multipass).to.be.true; - }); + var plugins = [ + { name: 'removeDoctype', active: false }, + { name: 'convertColors', params: { shorthex: false } }, + { name: 'removeRasterImages', params: { param: true } } + ].map(plugin => resolvePluginConfig(plugin, {})); + const removeDoctype = getPlugin('removeDoctype', plugins); + const convertColors = getPlugin('convertColors', plugins); + const removeRasterImages = getPlugin('removeRasterImages', plugins); it('removeDoctype plugin should be disabled', function() { expect(removeDoctype.active).to.be.false; @@ -90,21 +64,22 @@ describe('config', function() { describe('replace default config with custom', function() { - var config = CONFIG({ - multipass: true, - floatPrecision: 2, - plugins: [ - { name: 'cleanupNumericValues' } - ] - }), - cleanupNumericValues = getPlugin('cleanupNumericValues', config.plugins); + const config = { + multipass: true, + floatPrecision: 2, + plugins: [ + { name: 'cleanupNumericValues' } + ] + } + const plugins = config.plugins.map(plugin => resolvePluginConfig(plugin, config)); + const cleanupNumericValues = getPlugin('cleanupNumericValues', plugins); it('should have "multipass"', function() { expect(config.multipass).to.be.true; }); it('config.plugins should have length 1', function() { - expect(config.plugins).to.have.length(1); + expect(plugins).to.have.length(1); }); it('cleanupNumericValues plugin should be enabled', function() { @@ -121,16 +96,14 @@ describe('config', function() { describe('custom plugins', function() { describe('extend config with custom plugin', function() { - var config = CONFIG({ - plugins: [ - { - name: 'aCustomPlugin', - type: 'perItem', - fn: function() { } - } - ] - }), - customPlugin = getPlugin('aCustomPlugin', config.plugins); + const plugins = [ + { + name: 'aCustomPlugin', + type: 'perItem', + fn: function() { } + } + ].map(plugin => resolvePluginConfig(plugin, {})); + const customPlugin = getPlugin('aCustomPlugin', plugins); it('custom plugin should be enabled', function() { expect(customPlugin.active).to.be.true; @@ -143,19 +116,17 @@ describe('config', function() { describe('replace default config with custom plugin', function() { - var config = CONFIG({ - plugins: [ - { - name: 'aCustomPlugin', - type: 'perItem', - fn: function() { } - } - ] - }), - customPlugin = getPlugin('aCustomPlugin', config.plugins); + const plugins = [ + { + name: 'aCustomPlugin', + type: 'perItem', + fn: function() { } + } + ].map(plugin => resolvePluginConfig(plugin, {})); + const customPlugin = getPlugin('aCustomPlugin', plugins); it('config.plugins should have length 1', function() { - expect(config.plugins).to.have.length(1); + expect(plugins).to.have.length(1); }); it('custom plugin should be enabled', function() { @@ -173,18 +144,10 @@ describe('config', function() { }); function getPlugin(name, plugins) { - - var found; - - plugins.some(function(group) { - return group.some(function(plugin) { - if (plugin.name === name) { - found = plugin; - return true; - } - }); + return plugins.find(function(plugin) { + if (plugin.name === name) { + return plugin; + } }); - return found; - }