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
|
* @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;
|
||||||
|
@ -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;
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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');
|
||||||
|
@ -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;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user