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

Implement preset-default plugin (#1513)

I saw complaints about `extendDefaultPlugins` api

- it cannot be used when svgo is installed globally
- it requires svgo to be installed when using svgo-loader or svgo-jsx
- it prevents using serializable config formats like json

In this diff I introduced the new plugin which is a bundle of all
default plugins.

```js
module.exports = {
  plugins: [
    'preset_default',
    // or
    {
      name: 'preset_default',
      floatPrecision: 4,
      overrides: {
        convertPathData: {
          applyTransforms: false
        }
      }
    }
  ]
}
```
This commit is contained in:
Bogdan Chadkin
2021-08-13 19:07:08 +03:00
committed by GitHub
parent 9e578b515a
commit 07f8d606e0
60 changed files with 522 additions and 288 deletions

203
README.md
View File

@ -2,7 +2,6 @@
<img src="./logo/logo-web.svg" width="348.61" height="100" alt="SVGO logo"/>
</div>
## SVGO [![npm version](https://img.shields.io/npm/v/svgo)](https://npmjs.org/package/svgo) [![Discord](https://img.shields.io/discord/815166721315831868)](https://discord.gg/z8jX8NYxrE)
**SVG O**ptimizer is a Node.js-based tool for optimizing SVG vector graphics files.
@ -53,8 +52,8 @@ module.exports = {
js2svg: {
indent: 2, // string with spaces or number of spaces. 4 by default
pretty: true, // boolean, false by default
}
}
},
};
```
SVGO has a plugin-based architecture, so almost every optimization is a separate plugin.
@ -67,97 +66,83 @@ module.exports = {
'builtinPluginName',
// or by expanded version
{
name: 'builtinPluginName'
name: 'builtinPluginName',
},
// some plugins allow/require to pass options
{
name: 'builtinPluginName',
params: {
optionName: 'optionValue'
}
}
]
}
optionName: 'optionValue',
},
},
],
};
```
The default list is fully overridden if the `plugins` field is specified. To extend the default
list use the `extendDefaultPlugins` utility:
```js
const { extendDefaultPlugins } = require('svgo');
module.exports = {
plugins: extendDefaultPlugins([
{
name: 'builtinPluginName',
params: {
optionName: 'optionValue'
}
}
])
}
```
To disable one of the default plugins use the `active` field:
```js
const { extendDefaultPlugins } = require('svgo');
module.exports = {
plugins: extendDefaultPlugins([
{
name: 'builtinPluginName',
active: false
}
])
}
```
See the list of the default plugins:
The default preset of plugins is fully overridden if the `plugins` field is specified.
Use `preset-default` plugin to customize plugins options.
```js
module.exports = {
plugins: [
'removeDoctype',
'removeXMLProcInst',
'removeComments',
'removeMetadata',
'removeEditorsNSData',
'cleanupAttrs',
'mergeStyles',
'inlineStyles',
'minifyStyles',
'cleanupIDs',
'removeUselessDefs',
'cleanupNumericValues',
'convertColors',
'removeUnknownsAndDefaults',
'removeNonInheritableGroupAttrs',
'removeUselessStrokeAndFill',
'removeViewBox',
'cleanupEnableBackground',
'removeHiddenElems',
'removeEmptyText',
'convertShapeToPath',
'convertEllipseToCircle',
'moveElemsAttrsToGroup',
'moveGroupAttrsToElems',
'collapseGroups',
'convertPathData',
'convertTransform',
'removeEmptyAttrs',
'removeEmptyContainers',
'mergePaths',
'removeUnusedNS',
'sortDefsChildren',
'removeTitle',
'removeDesc'
]
}
{
name: 'preset-default',
params: {
overrides: {
// customize options
builtinPluginName: {
optionName: 'optionValue',
},
// or disable plugins
anotherBuiltinPlugin: false,
},
},
},
],
};
```
Default preset includes the following list of plugins:
- removeDoctype
- removeXMLProcInst
- removeComments
- removeMetadata
- removeEditorsNSData
- cleanupAttrs
- mergeStyles
- inlineStyles
- minifyStyles
- cleanupIDs
- removeUselessDefs
- cleanupNumericValues
- convertColors
- removeUnknownsAndDefaults
- removeNonInheritableGroupAttrs
- removeUselessStrokeAndFill
- removeViewBox
- cleanupEnableBackground
- removeHiddenElems
- removeEmptyText
- convertShapeToPath
- convertEllipseToCircle
- moveElemsAttrsToGroup
- moveGroupAttrsToElems
- collapseGroups
- convertPathData
- convertTransform
- removeEmptyAttrs
- removeEmptyContainers
- mergePaths
- removeUnusedNS
- sortDefsChildren
- removeTitle
- removeDesc
It's also possible to specify a custom plugin:
```js
const anotherCustomPlugin = require('./another-custom-plugin.js')
const anotherCustomPlugin = require('./another-custom-plugin.js');
module.exports = {
plugins: [
{
@ -166,11 +151,11 @@ module.exports = {
params: {
optionName: 'optionValue',
},
fn: (ast, params, info) => {}
fn: (ast, params, info) => {},
},
anotherCustomPlugin
]
}
anotherCustomPlugin,
],
};
```
## API usage
@ -187,9 +172,9 @@ const result = optimize(svgString, {
// optional but recommended field
path: 'path-to.svg',
// all config fields are also available here
multipass: true
})
const optimizedSvgString = result.data
multipass: true,
});
const optimizedSvgString = result.data;
```
### loadConfig
@ -198,16 +183,16 @@ If you write a tool on top of SVGO you might need a way to load SVGO config.
```js
const { loadConfig } = require('svgo');
const config = await loadConfig()
const config = await loadConfig();
// you can also specify a relative or absolute path and customize the current working directory
const config = await loadConfig(configFile, cwd)
const config = await loadConfig(configFile, cwd);
```
## Built-in plugins
| Plugin | Description | Default |
| ------ | ----------- | ------- |
| ------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- |
| [cleanupAttrs](https://github.com/svg/svgo/blob/master/plugins/cleanupAttrs.js) | cleanup attributes from newlines, trailing, and repeating spaces | `enabled` |
| [mergeStyles](https://github.com/svg/svgo/blob/master/plugins/mergeStyles.js) | merge multiple style elements into one | `enabled` |
| [inlineStyles](https://github.com/svg/svgo/blob/master/plugins/inlineStyles.js) | move and merge styles from `<style>` elements to element `style` attributes | `enabled` |
@ -261,31 +246,31 @@ const config = await loadConfig(configFile, cwd)
## Other Ways to Use SVGO
* as a web app [SVGOMG](https://jakearchibald.github.io/svgomg/)
* as a GitHub Action [SVGO Action](https://github.com/marketplace/actions/svgo-action)
* as a Grunt task [grunt-svgmin](https://github.com/sindresorhus/grunt-svgmin)
* as a Gulp task [gulp-svgmin](https://github.com/ben-eb/gulp-svgmin)
* as a Mimosa module [mimosa-minify-svg](https://github.com/dbashford/mimosa-minify-svg)
* as an OSX Folder Action [svgo-osx-folder-action](https://github.com/svg/svgo-osx-folder-action)
* as a webpack loader [image-webpack-loader](https://github.com/tcoopman/image-webpack-loader)
* as a Telegram Bot [svgo_bot](https://github.com/maksugr/svgo_bot)
* as a PostCSS plugin [postcss-svgo](https://github.com/ben-eb/postcss-svgo)
* as an Inkscape plugin [inkscape-svgo](https://github.com/konsumer/inkscape-svgo)
* as a Sketch plugin - [svgo-compressor](https://github.com/BohemianCoding/svgo-compressor)
* as a macOS app - [Image Shrinker](https://image-shrinker.com)
* as a Rollup plugin - [rollup-plugin-svgo](https://github.com/porsager/rollup-plugin-svgo)
* as a VS Code plugin - [vscode-svgo](https://github.com/1000ch/vscode-svgo)
* as a Atom plugin - [atom-svgo](https://github.com/1000ch/atom-svgo)
* as a Sublime plugin - [Sublime-svgo](https://github.com/1000ch/Sublime-svgo)
* as a Figma plugin - [Advanced SVG Export](https://www.figma.com/c/plugin/782713260363070260/Advanced-SVG-Export)
* as a Linux app - [Oh My SVG](https://github.com/sonnyp/OhMySVG)
* as a Browser extension - [SVG Gobbler](https://github.com/rossmoody/svg-gobbler)
* as an API - [Vector Express](https://github.com/smidyo/vectorexpress-api#convertor-svgo)
- as a web app [SVGOMG](https://jakearchibald.github.io/svgomg/)
- as a GitHub Action [SVGO Action](https://github.com/marketplace/actions/svgo-action)
- as a Grunt task [grunt-svgmin](https://github.com/sindresorhus/grunt-svgmin)
- as a Gulp task [gulp-svgmin](https://github.com/ben-eb/gulp-svgmin)
- as a Mimosa module [mimosa-minify-svg](https://github.com/dbashford/mimosa-minify-svg)
- as an OSX Folder Action [svgo-osx-folder-action](https://github.com/svg/svgo-osx-folder-action)
- as a webpack loader [image-webpack-loader](https://github.com/tcoopman/image-webpack-loader)
- as a Telegram Bot [svgo_bot](https://github.com/maksugr/svgo_bot)
- as a PostCSS plugin [postcss-svgo](https://github.com/ben-eb/postcss-svgo)
- as an Inkscape plugin [inkscape-svgo](https://github.com/konsumer/inkscape-svgo)
- as a Sketch plugin - [svgo-compressor](https://github.com/BohemianCoding/svgo-compressor)
- as a macOS app - [Image Shrinker](https://image-shrinker.com)
- as a Rollup plugin - [rollup-plugin-svgo](https://github.com/porsager/rollup-plugin-svgo)
- as a VS Code plugin - [vscode-svgo](https://github.com/1000ch/vscode-svgo)
- as a Atom plugin - [atom-svgo](https://github.com/1000ch/atom-svgo)
- as a Sublime plugin - [Sublime-svgo](https://github.com/1000ch/Sublime-svgo)
- as a Figma plugin - [Advanced SVG Export](https://www.figma.com/c/plugin/782713260363070260/Advanced-SVG-Export)
- as a Linux app - [Oh My SVG](https://github.com/sonnyp/OhMySVG)
- as a Browser extension - [SVG Gobbler](https://github.com/rossmoody/svg-gobbler)
- as an API - [Vector Express](https://github.com/smidyo/vectorexpress-api#convertor-svgo)
## Donators
| [<img src="https://sheetjs.com/sketch128.png" width="80">](https://sheetjs.com/) | [<img src="https://raw.githubusercontent.com/fontello/fontello/master/fontello-image.svg" width="80">](https://fontello.com/) |
|:-:|:-:|
| :------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------: |
| [SheetJS LLC](https://sheetjs.com/) | [Fontello](https://fontello.com/) |
## License and Copyright

View File

@ -7,7 +7,7 @@ const {
} = require('./svgo/config.js');
const svg2js = require('./svgo/svg2js.js');
const js2svg = require('./svgo/js2svg.js');
const invokePlugins = require('./svgo/plugins.js');
const { invokePlugins } = require('./svgo/plugins.js');
const JSAPI = require('./svgo/jsAPI.js');
const { encodeSVGDatauri } = require('./svgo/tools.js');
@ -42,10 +42,12 @@ const optimize = (input, config) => {
"Invalid plugins list. Provided 'plugins' in config should be an array."
);
}
const resolvedPlugins = plugins.map((plugin) =>
resolvePluginConfig(plugin, config)
);
svgjs = invokePlugins(svgjs, info, resolvedPlugins);
const resolvedPlugins = plugins.map(resolvePluginConfig);
const globalOverrides = {};
if (config.floatPrecision != null) {
globalOverrides.floatPrecision = config.floatPrecision;
}
svgjs = invokePlugins(svgjs, info, resolvedPlugins, null, globalOverrides);
svgjs = js2svg(svgjs, config.js2svg);
if (svgjs.error) {
throw Error(svgjs.error);

122
lib/svgo.test.js Normal file
View File

@ -0,0 +1,122 @@
'use strict';
const { optimize } = require('./svgo.js');
test('allow to setup default preset', () => {
const svg = `
<?xml version="1.0" encoding="utf-8"?>
<svg viewBox="0 0 120 120">
<desc>
Not standard description
</desc>
<circle fill="#ff0000" cx="60" cy="60" r="50"/>
</svg>
`;
expect(
optimize(svg, {
plugins: ['preset-default'],
js2svg: { pretty: true, indent: 2 },
}).data
).toMatchInlineSnapshot(`
"<svg viewBox=\\"0 0 120 120\\">
<circle fill=\\"red\\" cx=\\"60\\" cy=\\"60\\" r=\\"50\\"/>
</svg>
"
`);
});
test('allow to disable and customize plugins in preset', () => {
const svg = `
<?xml version="1.0" encoding="utf-8"?>
<svg viewBox="0 0 120 120">
<desc>
Not standard description
</desc>
<circle fill="#ff0000" cx="60" cy="60" r="50"/>
</svg>
`;
expect(
optimize(svg, {
plugins: [
{
name: 'preset-default',
params: {
overrides: {
removeXMLProcInst: false,
removeDesc: {
removeAny: false,
},
},
},
},
],
js2svg: { pretty: true, indent: 2 },
}).data
).toMatchInlineSnapshot(`
"<?xml version=\\"1.0\\" encoding=\\"utf-8\\"?>
<svg viewBox=\\"0 0 120 120\\">
<desc>
Not standard description
</desc>
<circle fill=\\"red\\" cx=\\"60\\" cy=\\"60\\" r=\\"50\\"/>
</svg>
"
`);
});
test('allow to customize precision for preset', () => {
const svg = `
<svg viewBox="0 0 120 120">
<circle fill="#ff0000" cx="60.444444" cy="60" r="50"/>
</svg>
`;
expect(
optimize(svg, {
plugins: [
{
name: 'preset-default',
params: {
floatPrecision: 4,
},
},
],
js2svg: { pretty: true, indent: 2 },
}).data
).toMatchInlineSnapshot(`
"<svg viewBox=\\"0 0 120 120\\">
<circle fill=\\"red\\" cx=\\"60.4444\\" cy=\\"60\\" r=\\"50\\"/>
</svg>
"
`);
});
test('plugin precision should override preset precision', () => {
const svg = `
<svg viewBox="0 0 120 120">
<circle fill="#ff0000" cx="60.444444" cy="60" r="50"/>
</svg>
`;
expect(
optimize(svg, {
plugins: [
{
name: 'preset-default',
params: {
floatPrecision: 4,
overrides: {
cleanupNumericValues: {
floatPrecision: 5,
},
},
},
},
],
js2svg: { pretty: true, indent: 2 },
}).data
).toMatchInlineSnapshot(`
"<svg viewBox=\\"0 0 120 120\\">
<circle fill=\\"red\\" cx=\\"60.44444\\" cy=\\"60\\" r=\\"50\\"/>
</svg>
"
`);
});

View File

@ -58,12 +58,30 @@ const defaultPlugins = pluginsOrder.filter((name) => pluginsMap[name].active);
exports.defaultPlugins = defaultPlugins;
const extendDefaultPlugins = (plugins) => {
console.warn(
'\n"extendDefaultPlugins" utility is deprecated.\n' +
'Use "preset-default" plugin with overrides instead.\n' +
'For example:\n' +
`{\n` +
` name: 'preset-default',\n` +
` params: {\n` +
` overrides: {\n` +
` // customize plugin options\n` +
` convertShapeToPath: {\n` +
` convertArcs: true\n` +
` },\n` +
` // disable plugins\n` +
` convertPathData: false\n` +
` }\n` +
` }\n` +
`}\n`
);
const extendedPlugins = pluginsOrder.map((name) => ({
name,
active: pluginsMap[name].active,
}));
for (const plugin of plugins) {
const resolvedPlugin = resolvePluginConfig(plugin, {});
const resolvedPlugin = resolvePluginConfig(plugin);
const index = pluginsOrder.indexOf(resolvedPlugin.name);
if (index === -1) {
extendedPlugins.push(plugin);
@ -75,11 +93,8 @@ const extendDefaultPlugins = (plugins) => {
};
exports.extendDefaultPlugins = extendDefaultPlugins;
const resolvePluginConfig = (plugin, config) => {
const resolvePluginConfig = (plugin) => {
let configParams = {};
if ('floatPrecision' in config) {
configParams.floatPrecision = config.floatPrecision;
}
if (typeof plugin === 'string') {
// resolve builtin plugin specified as string
const pluginConfig = pluginsMap[plugin];

View File

@ -7,47 +7,40 @@ const { visit } = require('../xast.js');
*
* @module plugins
*
* @param {Object} data input data
* @param {Object} ast input ast
* @param {Object} info extra information
* @param {Array} plugins plugins object from config
* @return {Object} output data
* @return {Object} output ast
*/
module.exports = function (data, info, plugins) {
// Try to group sequential elements of plugins array
// to optimize ast traversing
const groups = [];
let prev;
const invokePlugins = (ast, info, plugins, overrides, globalOverrides) => {
for (const plugin of plugins) {
if (prev && plugin.type == prev[0].type) {
prev.push(plugin);
} else {
prev = [plugin];
groups.push(prev);
const override = overrides == null ? null : overrides[plugin.name];
if (override === false) {
continue;
}
const params = { ...plugin.params, ...globalOverrides, ...override };
if (plugin.type === 'perItem') {
ast = perItem(ast, info, plugin, params);
}
for (const group of groups) {
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;
case 'visitor':
for (const plugin of group) {
if (plugin.type === 'perItemReverse') {
ast = perItem(ast, info, plugin, params, true);
}
if (plugin.type === 'full') {
if (plugin.active) {
const visitor = plugin.fn(data, plugin.params, info);
visit(data, visitor);
ast = plugin.fn(ast, params, info);
}
}
break;
if (plugin.type === 'visitor') {
if (plugin.active) {
const visitor = plugin.fn(ast, params, info);
visit(ast, visitor);
}
}
return data;
}
return ast;
};
exports.invokePlugins = invokePlugins;
/**
* Direct or reverse per-item loop.
@ -58,53 +51,41 @@ module.exports = function (data, info, plugins) {
* @param {boolean} [reverse] reverse pass?
* @return {Object} output data
*/
function perItem(data, info, plugins, reverse) {
function perItem(data, info, plugin, params, reverse) {
function monkeys(items) {
items.children = items.children.filter(function (item) {
// reverse pass
if (reverse && item.children) {
monkeys(item);
}
// main filter
var filter = true;
for (var i = 0; filter && i < plugins.length; i++) {
var plugin = plugins[i];
if (plugin.active && plugin.fn(item, plugin.params, info) === false) {
filter = false;
let kept = true;
if (plugin.active) {
kept = plugin.fn(item, params, info) !== false;
}
}
// direct pass
if (!reverse && item.children) {
monkeys(item);
}
return filter;
return kept;
});
return items;
}
return monkeys(data);
}
/**
* "Full" plugins.
*
* @param {Object} data input data
* @param {Object} info extra information
* @param {Array} plugins plugins list to process
* @return {Object} output data
*/
function full(data, info, plugins) {
plugins.forEach(function (plugin) {
if (plugin.active) {
data = plugin.fn(data, plugin.params, info);
const createPreset = ({ name, plugins }) => {
return {
name,
type: 'full',
fn: (ast, params, info) => {
const { floatPrecision, overrides } = params;
const globalOverrides = {};
if (floatPrecision != null) {
globalOverrides.floatPrecision = floatPrecision;
}
});
return data;
}
return invokePlugins(ast, info, plugins, overrides, globalOverrides);
},
};
};
exports.createPreset = createPreset;

View File

@ -2,6 +2,8 @@
const { closestByName } = require('../lib/xast.js');
exports.name = 'addAttributesToSVGElement';
exports.type = 'perItem';
exports.active = false;

View File

@ -1,5 +1,7 @@
'use strict';
exports.name = 'addClassesToSVGElement';
exports.type = 'full';
exports.active = false;

View File

@ -1,5 +1,6 @@
'use strict';
exports.name = 'cleanupAttrs';
exports.type = 'visitor';
exports.active = true;
exports.description =

View File

@ -2,6 +2,8 @@
const { traverse } = require('../lib/xast.js');
exports.name = 'cleanupEnableBackground';
exports.type = 'full';
exports.active = true;

View File

@ -3,6 +3,8 @@
const { traverse, traverseBreak } = require('../lib/xast.js');
const { parseName } = require('../lib/svgo/tools.js');
exports.name = 'cleanupIDs';
exports.type = 'full';
exports.active = true;

View File

@ -2,6 +2,8 @@
const { removeLeadingZero } = require('../lib/svgo/tools.js');
exports.name = 'cleanupListOfValues';
exports.type = 'perItem';
exports.active = false;

View File

@ -1,5 +1,7 @@
'use strict';
exports.name = 'cleanupNumericValues';
exports.type = 'perItem';
exports.active = true;

View File

@ -2,6 +2,8 @@
const { inheritableAttrs, elemsGroups } = require('./_collections');
exports.name = 'collapseGroups';
exports.type = 'perItemReverse';
exports.active = true;

View File

@ -1,5 +1,7 @@
'use strict';
exports.name = 'convertColors';
exports.type = 'perItem';
exports.active = true;

View File

@ -1,5 +1,6 @@
'use strict';
exports.name = 'convertEllipseToCircle';
exports.type = 'visitor';
exports.active = true;
exports.description = 'converts non-eccentric <ellipse>s to <circle>s';

View File

@ -6,6 +6,7 @@ const { path2js, js2path } = require('./_path.js');
const { applyTransforms } = require('./_applyTransforms.js');
const { cleanupOutData } = require('../lib/svgo/tools');
exports.name = 'convertPathData';
exports.type = 'visitor';
exports.active = true;
exports.description =

View File

@ -3,6 +3,7 @@
const { stringifyPathData } = require('../lib/path.js');
const { detachNodeFromParent } = require('../lib/xast.js');
exports.name = 'convertShapeToPath';
exports.type = 'visitor';
exports.active = true;
exports.description = 'converts basic shapes to more compact path form';

View File

@ -1,5 +1,7 @@
'use strict';
exports.name = 'convertStyleToAttrs';
exports.type = 'perItem';
exports.active = false;

View File

@ -1,5 +1,7 @@
'use strict';
exports.name = 'convertTransform';
exports.type = 'perItem';
exports.active = true;

View File

@ -4,6 +4,8 @@ const csstree = require('css-tree');
const { querySelectorAll, closestByName } = require('../lib/xast.js');
const cssTools = require('../lib/css-tools');
exports.name = 'inlineStyles';
exports.type = 'full';
exports.active = true;

View File

@ -4,6 +4,7 @@ const { detachNodeFromParent } = require('../lib/xast.js');
const { collectStylesheet, computeStyle } = require('../lib/style.js');
const { path2js, js2path, intersects } = require('./_path.js');
exports.name = 'mergePaths';
exports.type = 'visitor';
exports.active = true;
exports.description = 'merges multiple paths in one if possible';

View File

@ -3,6 +3,7 @@
const { closestByName, detachNodeFromParent } = require('../lib/xast.js');
const JSAPI = require('../lib/svgo/jsAPI.js');
exports.name = 'mergeStyles';
exports.type = 'visitor';
exports.active = true;
exports.description = 'merge multiple style elements into one';

View File

@ -3,6 +3,8 @@
const csso = require('csso');
const { traverse } = require('../lib/xast.js');
exports.name = 'minifyStyles';
exports.type = 'full';
exports.active = true;

View File

@ -2,6 +2,8 @@
const { inheritableAttrs, pathElems } = require('./_collections');
exports.name = 'moveElemsAttrsToGroup';
exports.type = 'perItemReverse';
exports.active = true;

View File

@ -2,6 +2,8 @@
const { pathElems, referencesProps } = require('./_collections.js');
exports.name = 'moveGroupAttrsToElems';
exports.type = 'perItem';
exports.active = true;

View File

@ -1,5 +1,9 @@
'use strict';
// builtin presets
exports['preset-default'] = require('./preset-default.js');
// builtin plugins
exports.addAttributesToSVGElement = require('./addAttributesToSVGElement.js');
exports.addClassesToSVGElement = require('./addClassesToSVGElement.js');
exports.cleanupAttrs = require('./cleanupAttrs.js');

View File

@ -1,5 +1,7 @@
'use strict';
exports.name = 'prefixIds';
exports.type = 'perItem';
exports.active = false;

80
plugins/preset-default.js Normal file
View File

@ -0,0 +1,80 @@
'use strict';
const { createPreset } = require('../lib/svgo/plugins.js');
const removeDoctype = require('./removeDoctype.js');
const removeXMLProcInst = require('./removeXMLProcInst.js');
const removeComments = require('./removeComments.js');
const removeMetadata = require('./removeMetadata.js');
const removeEditorsNSData = require('./removeEditorsNSData.js');
const cleanupAttrs = require('./cleanupAttrs.js');
const mergeStyles = require('./mergeStyles.js');
const inlineStyles = require('./inlineStyles.js');
const minifyStyles = require('./minifyStyles.js');
const cleanupIDs = require('./cleanupIDs.js');
const removeUselessDefs = require('./removeUselessDefs.js');
const cleanupNumericValues = require('./cleanupNumericValues.js');
const convertColors = require('./convertColors.js');
const removeUnknownsAndDefaults = require('./removeUnknownsAndDefaults.js');
const removeNonInheritableGroupAttrs = require('./removeNonInheritableGroupAttrs.js');
const removeUselessStrokeAndFill = require('./removeUselessStrokeAndFill.js');
const removeViewBox = require('./removeViewBox.js');
const cleanupEnableBackground = require('./cleanupEnableBackground.js');
const removeHiddenElems = require('./removeHiddenElems.js');
const removeEmptyText = require('./removeEmptyText.js');
const convertShapeToPath = require('./convertShapeToPath.js');
const convertEllipseToCircle = require('./convertEllipseToCircle.js');
const moveElemsAttrsToGroup = require('./moveElemsAttrsToGroup.js');
const moveGroupAttrsToElems = require('./moveGroupAttrsToElems.js');
const collapseGroups = require('./collapseGroups.js');
const convertPathData = require('./convertPathData.js');
const convertTransform = require('./convertTransform.js');
const removeEmptyAttrs = require('./removeEmptyAttrs.js');
const removeEmptyContainers = require('./removeEmptyContainers.js');
const mergePaths = require('./mergePaths.js');
const removeUnusedNS = require('./removeUnusedNS.js');
const sortDefsChildren = require('./sortDefsChildren.js');
const removeTitle = require('./removeTitle.js');
const removeDesc = require('./removeDesc.js');
const presetDefault = createPreset({
name: 'presetDefault',
plugins: [
removeDoctype,
removeXMLProcInst,
removeComments,
removeMetadata,
removeEditorsNSData,
cleanupAttrs,
mergeStyles,
inlineStyles,
minifyStyles,
cleanupIDs,
removeUselessDefs,
cleanupNumericValues,
convertColors,
removeUnknownsAndDefaults,
removeNonInheritableGroupAttrs,
removeUselessStrokeAndFill,
removeViewBox,
cleanupEnableBackground,
removeHiddenElems,
removeEmptyText,
convertShapeToPath,
convertEllipseToCircle,
moveElemsAttrsToGroup,
moveGroupAttrsToElems,
collapseGroups,
convertPathData,
convertTransform,
removeEmptyAttrs,
removeEmptyContainers,
mergePaths,
removeUnusedNS,
sortDefsChildren,
removeTitle,
removeDesc,
],
});
module.exports = presetDefault;

View File

@ -1,5 +1,7 @@
'use strict';
exports.name = 'removeAttributesBySelector';
exports.type = 'perItem';
exports.active = false;

View File

@ -2,6 +2,8 @@
var DEFAULT_SEPARATOR = ':';
exports.name = 'removeAttrs';
exports.type = 'perItem';
exports.active = false;

View File

@ -2,6 +2,7 @@
const { detachNodeFromParent } = require('../lib/xast.js');
exports.name = 'removeComments';
exports.type = 'visitor';
exports.active = true;
exports.description = 'removes comments';

View File

@ -2,6 +2,7 @@
const { detachNodeFromParent } = require('../lib/xast.js');
exports.name = 'removeDesc';
exports.type = 'visitor';
exports.active = true;
exports.description = 'removes <desc>';

View File

@ -1,5 +1,7 @@
'use strict';
exports.name = 'removeDimensions';
exports.type = 'perItem';
exports.active = false;

View File

@ -2,6 +2,7 @@
const { detachNodeFromParent } = require('../lib/xast.js');
exports.name = 'removeDoctype';
exports.type = 'visitor';
exports.active = true;
exports.description = 'removes doctype declaration';

View File

@ -3,6 +3,8 @@
const { parseName } = require('../lib/svgo/tools.js');
const { editorNamespaces } = require('./_collections');
exports.name = 'removeEditorsNSData';
exports.type = 'perItem';
exports.active = true;

View File

@ -1,5 +1,7 @@
'use strict';
exports.name = 'removeElementsByAttr';
exports.type = 'perItem';
exports.active = false;

View File

@ -2,6 +2,8 @@
const { attrsGroups } = require('./_collections.js');
exports.name = 'removeEmptyAttrs';
exports.type = 'perItem';
exports.active = true;

View File

@ -2,6 +2,8 @@
const { elemsGroups } = require('./_collections');
exports.name = 'removeEmptyContainers';
exports.type = 'perItemReverse';
exports.active = true;

View File

@ -2,6 +2,7 @@
const { detachNodeFromParent } = require('../lib/xast.js');
exports.name = 'removeEmptyText';
exports.type = 'visitor';
exports.active = true;
exports.description = 'removes empty <text> elements';

View File

@ -8,6 +8,7 @@ const {
const { collectStylesheet, computeStyle } = require('../lib/style.js');
const { parsePathData } = require('../lib/path.js');
exports.name = 'removeHiddenElems';
exports.type = 'visitor';
exports.active = true;
exports.description =

View File

@ -2,6 +2,7 @@
const { detachNodeFromParent } = require('../lib/xast.js');
exports.name = 'removeMetadata';
exports.type = 'visitor';
exports.active = true;
exports.description = 'removes <metadata>';

View File

@ -1,5 +1,7 @@
'use strict';
exports.name = 'removeNonInheritableGroupAttrs';
exports.type = 'perItem';
exports.active = true;

View File

@ -1,5 +1,7 @@
'use strict';
exports.name = 'removeOffCanvasPaths';
exports.type = 'perItem';
exports.active = false;

View File

@ -2,6 +2,7 @@
const { detachNodeFromParent } = require('../lib/xast.js');
exports.name = 'removeRasterImages';
exports.type = 'visitor';
exports.active = false;
exports.description = 'removes raster images (disabled by default)';

View File

@ -2,6 +2,7 @@
const { detachNodeFromParent } = require('../lib/xast.js');
exports.name = 'removeScriptElement';
exports.type = 'visitor';
exports.active = false;
exports.description = 'removes <script> elements (disabled by default)';

View File

@ -2,6 +2,7 @@
const { detachNodeFromParent } = require('../lib/xast.js');
exports.name = 'removeStyleElement';
exports.type = 'visitor';
exports.active = false;
exports.description = 'removes <style> element (disabled by default)';

View File

@ -2,6 +2,7 @@
const { detachNodeFromParent } = require('../lib/xast.js');
exports.name = 'removeTitle';
exports.type = 'visitor';
exports.active = true;
exports.description = 'removes <title>';

View File

@ -2,6 +2,8 @@
const { parseName } = require('../lib/svgo/tools.js');
exports.name = 'removeUnknownsAndDefaults';
exports.type = 'perItem';
exports.active = true;

View File

@ -3,6 +3,8 @@
const { traverse } = require('../lib/xast.js');
const { parseName } = require('../lib/svgo/tools.js');
exports.name = 'removeUnusedNS';
exports.type = 'full';
exports.active = true;

View File

@ -2,6 +2,8 @@
const { elemsGroups } = require('./_collections');
exports.name = 'removeUselessDefs';
exports.type = 'perItem';
exports.active = true;

View File

@ -1,5 +1,7 @@
'use strict';
exports.name = 'removeUselessStrokeAndFill';
exports.type = 'perItem';
exports.active = true;

View File

@ -2,6 +2,8 @@
const { closestByName } = require('../lib/xast.js');
exports.name = 'removeViewBox';
exports.type = 'perItem';
exports.active = true;

View File

@ -1,5 +1,7 @@
'use strict';
exports.name = 'removeXMLNS';
exports.type = 'perItem';
exports.active = false;

View File

@ -2,6 +2,7 @@
const { detachNodeFromParent } = require('../lib/xast.js');
exports.name = 'removeXMLProcInst';
exports.type = 'visitor';
exports.active = true;
exports.description = 'removes XML processing instructions';

View File

@ -3,6 +3,8 @@
const { traverse } = require('../lib/xast.js');
const JSAPI = require('../lib/svgo/jsAPI');
exports.name = 'reusePaths';
exports.type = 'full';
exports.active = false;

View File

@ -2,6 +2,8 @@
const { parseName } = require('../lib/svgo/tools.js');
exports.name = 'sortAttrs';
exports.type = 'perItem';
exports.active = false;

View File

@ -1,5 +1,7 @@
'use strict';
exports.name = 'sortDefsChildren';
exports.type = 'perItem';
exports.active = true;

View File

@ -13,7 +13,7 @@ describe('config', function () {
{ name: 'removeDoctype', active: false },
{ name: 'convertColors', params: { shorthex: false } },
{ name: 'removeRasterImages', params: { param: true } },
].map((plugin) => resolvePluginConfig(plugin, {}));
].map((plugin) => resolvePluginConfig(plugin));
const removeDoctype = getPlugin('removeDoctype', plugins);
const convertColors = getPlugin('convertColors', plugins);
const removeRasterImages = getPlugin('removeRasterImages', plugins);
@ -62,19 +62,15 @@ describe('config', function () {
describe('replace default config with custom', function () {
const config = {
multipass: true,
floatPrecision: 2,
plugins: [
'convertPathData',
{ name: 'cleanupNumericValues' },
{ name: 'customPlugin', fn: () => {} },
],
};
const plugins = config.plugins.map((plugin) =>
resolvePluginConfig(plugin, config)
);
const plugins = config.plugins.map((plugin) => resolvePluginConfig(plugin));
const cleanupNumericValues = getPlugin('cleanupNumericValues', plugins);
const convertPathData = getPlugin('convertPathData', plugins);
const customPlugin = getPlugin('customPlugin', plugins);
it('should have "multipass"', function () {
expect(config.multipass).toEqual(true);
@ -88,12 +84,6 @@ describe('config', function () {
expect(cleanupNumericValues.active).toEqual(true);
expect(convertPathData.active).toEqual(true);
});
it('plugins should inherit floatPrecision top level config', function () {
expect(cleanupNumericValues.params.floatPrecision).toEqual(2);
expect(convertPathData.params.floatPrecision).toEqual(2);
expect(customPlugin.params.floatPrecision).toEqual(2);
});
});
describe('custom plugins', function () {
@ -104,7 +94,7 @@ describe('config', function () {
type: 'perItem',
fn: function () {},
},
].map((plugin) => resolvePluginConfig(plugin, {}));
].map((plugin) => resolvePluginConfig(plugin));
const customPlugin = getPlugin('aCustomPlugin', plugins);
it('custom plugin should be enabled', function () {
@ -123,7 +113,7 @@ describe('config', function () {
type: 'perItem',
fn: function () {},
},
].map((plugin) => resolvePluginConfig(plugin, {}));
].map((plugin) => resolvePluginConfig(plugin));
const customPlugin = getPlugin('aCustomPlugin', plugins);
it('config.plugins should have length 1', function () {
@ -167,20 +157,17 @@ describe('config', function () {
});
it('should activate inactive by default plugins', () => {
const removeAttrsPlugin = resolvePluginConfig(
extendedPlugins[removeAttrsIndex],
{}
extendedPlugins[removeAttrsIndex]
);
const cleanupIDsPlugin = resolvePluginConfig(
extendedPlugins[cleanupIDsIndex],
{}
extendedPlugins[cleanupIDsIndex]
);
expect(removeAttrsPlugin.active).toEqual(true);
expect(cleanupIDsPlugin.active).toEqual(true);
});
it('should leave not extended inactive plugins to be inactive', () => {
const inactivePlugin = resolvePluginConfig(
extendedPlugins.find((item) => item.name === 'addClassesToSVGElement'),
{}
extendedPlugins.find((item) => item.name === 'addClassesToSVGElement')
);
expect(inactivePlugin.active).toEqual(false);
});
@ -189,18 +176,6 @@ describe('config', function () {
'customPlugin'
);
});
it('should pass global floatPrecision when plugin one not specified', () => {
const convertPathDataPlugin = resolvePluginConfig(
extendedPlugins.find((item) => item.name === 'convertPathData'),
{ floatPrecision: 1 }
);
const convertTransformPlugin = resolvePluginConfig(
extendedPlugins.find((item) => item.name === 'convertTransform'),
{}
);
expect(convertPathDataPlugin.params.floatPrecision).toEqual(1);
expect(convertTransformPlugin.params.floatPrecision).toEqual(3);
});
});
describe('config', () => {

View File

@ -3,7 +3,7 @@
const fs = require('fs');
const path = require('path');
const { EOL } = require('os');
const { optimize, extendDefaultPlugins } = require('../../lib/svgo.js');
const { optimize } = require('../../lib/svgo.js');
const regEOL = new RegExp(EOL, 'g');
@ -37,11 +37,7 @@ describe('svgo', () => {
const [original, expected] = await parseFixture('multipass-prefix-ids.svg');
const result = optimize(original, {
multipass: true,
plugins: extendDefaultPlugins([
{
name: 'prefixIds',
},
]),
plugins: ['preset-default', 'prefixIds'],
});
expect(normalize(result.data)).toEqual(expected);
});

View File

@ -12,8 +12,8 @@
},
"include": ["lib/**/*", "plugins/**/*", "test/**/*"],
"exclude": [
"**/*.test.js",
"lib/svgo-node.js",
"lib/style.test.js",
"lib/svgo/coa.js",
"lib/svgo/config.js",
"lib/svgo/js2svg.js",
@ -62,20 +62,11 @@
"plugins/removeXMLNS.js",
"plugins/reusePaths.js",
"plugins/sortDefsChildren.js",
"plugins/preset-default.js",
"lib/svgo/jsAPI.js",
"test/browser.js",
"test/coa/_index.test.js",
"test/config/_index.test.js",
"test/config/fixtures/invalid-array.js",
"test/config/fixtures/invalid-null.js",
"test/config/fixtures/invalid-string.js",
"test/config/fixtures/invalid/svgo.config.js",
"test/config/fixtures/module-not-found.js",
"test/config/fixtures/one/two/config.js",
"test/jsapi/_index.test.js",
"test/plugins/_index.test.js",
"test/config/fixtures/**/*.js",
"test/regression.js",
"test/svg2js/_index.test.js",
"test/svgo/_index.test.js"
"lib/svgo/plugins.js"
]
}