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:
78
lib/svgo.js
78
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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -1,3 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
exports.addAttributesToSVGElement = require('./addAttributesToSVGElement.js');
|
||||
exports.addClassesToSVGElement = require('./addClassesToSVGElement.js');
|
||||
exports.cleanupAttrs = require('./cleanupAttrs.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;
|
||||
|
||||
}
|
||||
|
Reference in New Issue
Block a user