1
0
mirror of https://github.com/svg/svgo.git synced 2025-07-29 20:21:14 +03:00

Move config processing into optimize

This commit is contained in:
Bogdan Chadkin
2021-02-15 16:57:01 +03:00
parent 1b776ffea5
commit 225bfbb053
5 changed files with 118 additions and 204 deletions

View File

@ -10,48 +10,52 @@
* @license MIT https://raw.githubusercontent.com/svg/svgo/master/LICENSE * @license MIT https://raw.githubusercontent.com/svg/svgo/master/LICENSE
*/ */
var CONFIG = require('./svgo/config.js'), const { resolvePluginConfig } = require('./svgo/config.js');
SVG2JS = require('./svgo/svg2js.js'), const SVG2JS = require('./svgo/svg2js.js');
PLUGINS = require('./svgo/plugins.js'), const PLUGINS = require('./svgo/plugins.js');
JSAPI = require('./svgo/jsAPI.js'), const JSAPI = require('./svgo/jsAPI.js');
encodeSVGDatauri = require('./svgo/tools.js').encodeSVGDatauri, const { encodeSVGDatauri } = require('./svgo/tools.js');
JS2SVG = require('./svgo/js2svg.js'); const JS2SVG = require('./svgo/js2svg.js');
var SVGO = function(config) { var SVGO = function(config = {}) {
this.config = CONFIG(config); this.config = config;
}; };
SVGO.prototype.optimize = function(svgstr, info = {}) { SVGO.prototype.optimize = function(svgstr, info = {}) {
const config = this.config; const config = this.config;
if (config.error) { if (typeof config !== 'object') {
throw Error(config.error); 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; svgjs = JS2SVG(svgjs, config.js2svg);
let prevResultSize = Number.POSITIVE_INFINITY; if (svgjs.error) {
let svgjs = null; throw Error(svgjs.error);
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;
}
} }
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); return new JSAPI(data);
}; };
SVGO.Config = CONFIG;
module.exports = SVGO; module.exports = SVGO;
// Offer ES module interop compatibility. // Offer ES module interop compatibility.
module.exports.default = SVGO; module.exports.default = SVGO;

View File

@ -2,52 +2,11 @@
const pluginsMap = require('../../plugins/plugins.js'); const pluginsMap = require('../../plugins/plugins.js');
/** const resolvePluginConfig = (plugin, config) => {
* Read and/or extend/replace default config file, let configParams = {};
* prepare and optimize plugins array. if ('floatPrecision' in config) {
* configParams.floatPrecision = config.floatPrecision;
* @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 => {
if (typeof plugin === 'string') { if (typeof plugin === 'string') {
// resolve builtin plugin specified as string // resolve builtin plugin specified as string
const pluginConfig = pluginsMap[plugin]; const pluginConfig = pluginsMap[plugin];
@ -58,7 +17,7 @@ const resolvePluginConfig = plugin => {
...pluginConfig, ...pluginConfig,
name: plugin, name: plugin,
active: true, active: true,
params: { ...pluginConfig.params } params: { ...pluginConfig.params, ...configParams }
}; };
} }
if (typeof plugin === 'object' && plugin != null) { if (typeof plugin === 'object' && plugin != null) {
@ -70,7 +29,7 @@ const resolvePluginConfig = plugin => {
return { return {
active: true, active: true,
...plugin, ...plugin,
params: { ...plugin.params } params: { configParams, ...plugin.params }
}; };
} else { } else {
// resolve builtin plugin specified as object without implementation // resolve builtin plugin specified as object without implementation
@ -82,30 +41,10 @@ const resolvePluginConfig = plugin => {
...pluginConfig, ...pluginConfig,
active: true, active: true,
...plugin, ...plugin,
params: { ...pluginConfig.params, ...plugin.params } params: { ...pluginConfig.params, ...configParams, ...plugin.params }
}; };
} }
} }
return null; return null;
}; };
exports.resolvePluginConfig = resolvePluginConfig;
/**
* 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;
}, []);
}

View File

@ -11,25 +11,33 @@
* @return {Object} output data * @return {Object} output data
*/ */
module.exports = function(data, info, plugins) { module.exports = function(data, info, plugins) {
const perItemPlugins = [];
plugins.forEach(function(group) { const perItemReversePlugins = [];
const fullPlugins = [];
switch(group[0].type) { // Try to group sequential elements of plugins array
case 'perItem': for (const plugin of plugins) {
data = perItem(data, info, group); switch(plugin.type) {
break; case 'perItem':
case 'perItemReverse': perItemPlugins.push(plugin);
data = perItem(data, info, group, true); break;
break; case 'perItemReverse':
case 'full': perItemReversePlugins.push(plugin);
data = full(data, info, group); break;
break; case 'full':
} fullPlugins.push(plugin);
break;
}); }
}
return data; 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;
}; };
/** /**

View File

@ -1,3 +1,5 @@
'use strict';
exports.addAttributesToSVGElement = require('./addAttributesToSVGElement.js'); exports.addAttributesToSVGElement = require('./addAttributesToSVGElement.js');
exports.addClassesToSVGElement = require('./addClassesToSVGElement.js'); exports.addClassesToSVGElement = require('./addClassesToSVGElement.js');
exports.cleanupAttrs = require('./cleanupAttrs.js'); exports.cleanupAttrs = require('./cleanupAttrs.js');

View File

@ -1,46 +1,20 @@
'use strict'; 'use strict';
const { expect } = require('chai'); const { expect } = require('chai');
const { resolvePluginConfig } = require('../../lib/svgo/config.js');
var CONFIG = require('../../lib/svgo/config');
describe('config', function() { 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() { describe('extend config with object', function() {
var config = CONFIG({ var plugins = [
multipass: true, { name: 'removeDoctype', active: false },
plugins: [ { name: 'convertColors', params: { shorthex: false } },
{ name: 'removeDoctype', active: false }, { name: 'removeRasterImages', params: { param: true } }
{ name: 'convertColors', params: { shorthex: false } }, ].map(plugin => resolvePluginConfig(plugin, {}));
{ name: 'removeRasterImages', params: { param: true } } const removeDoctype = getPlugin('removeDoctype', plugins);
] const convertColors = getPlugin('convertColors', plugins);
}), const removeRasterImages = getPlugin('removeRasterImages', plugins);
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;
});
it('removeDoctype plugin should be disabled', function() { it('removeDoctype plugin should be disabled', function() {
expect(removeDoctype.active).to.be.false; expect(removeDoctype.active).to.be.false;
@ -90,21 +64,22 @@ describe('config', function() {
describe('replace default config with custom', function() { describe('replace default config with custom', function() {
var config = CONFIG({ const config = {
multipass: true, multipass: true,
floatPrecision: 2, floatPrecision: 2,
plugins: [ plugins: [
{ name: 'cleanupNumericValues' } { name: 'cleanupNumericValues' }
] ]
}), }
cleanupNumericValues = getPlugin('cleanupNumericValues', config.plugins); const plugins = config.plugins.map(plugin => resolvePluginConfig(plugin, config));
const cleanupNumericValues = getPlugin('cleanupNumericValues', plugins);
it('should have "multipass"', function() { it('should have "multipass"', function() {
expect(config.multipass).to.be.true; expect(config.multipass).to.be.true;
}); });
it('config.plugins should have length 1', function() { 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() { it('cleanupNumericValues plugin should be enabled', function() {
@ -121,16 +96,14 @@ describe('config', function() {
describe('custom plugins', function() { describe('custom plugins', function() {
describe('extend config with custom plugin', function() { describe('extend config with custom plugin', function() {
var config = CONFIG({ const plugins = [
plugins: [ {
{ name: 'aCustomPlugin',
name: 'aCustomPlugin', type: 'perItem',
type: 'perItem', fn: function() { }
fn: function() { } }
} ].map(plugin => resolvePluginConfig(plugin, {}));
] const customPlugin = getPlugin('aCustomPlugin', plugins);
}),
customPlugin = getPlugin('aCustomPlugin', config.plugins);
it('custom plugin should be enabled', function() { it('custom plugin should be enabled', function() {
expect(customPlugin.active).to.be.true; expect(customPlugin.active).to.be.true;
@ -143,19 +116,17 @@ describe('config', function() {
describe('replace default config with custom plugin', function() { describe('replace default config with custom plugin', function() {
var config = CONFIG({ const plugins = [
plugins: [ {
{ name: 'aCustomPlugin',
name: 'aCustomPlugin', type: 'perItem',
type: 'perItem', fn: function() { }
fn: function() { } }
} ].map(plugin => resolvePluginConfig(plugin, {}));
] const customPlugin = getPlugin('aCustomPlugin', plugins);
}),
customPlugin = getPlugin('aCustomPlugin', config.plugins);
it('config.plugins should have length 1', function() { 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() { it('custom plugin should be enabled', function() {
@ -173,18 +144,10 @@ describe('config', function() {
}); });
function getPlugin(name, plugins) { function getPlugin(name, plugins) {
return plugins.find(function(plugin) {
var found; if (plugin.name === name) {
return plugin;
plugins.some(function(group) { }
return group.some(function(plugin) {
if (plugin.name === name) {
found = plugin;
return true;
}
});
}); });
return found;
} }