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
*/
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;

View File

@ -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;

View File

@ -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;
};
/**

View File

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

View File

@ -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;
}