svgo-v2 2.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +294 -0
- package/bin/svgo +10 -0
- package/dist/svgo.browser.js +1 -0
- package/lib/css-tools.js +239 -0
- package/lib/parser.js +259 -0
- package/lib/path.js +347 -0
- package/lib/stringifier.js +326 -0
- package/lib/style.js +283 -0
- package/lib/svgo/coa.js +517 -0
- package/lib/svgo/config.js +138 -0
- package/lib/svgo/css-class-list.js +72 -0
- package/lib/svgo/css-select-adapter.d.ts +2 -0
- package/lib/svgo/css-select-adapter.js +120 -0
- package/lib/svgo/css-style-declaration.js +232 -0
- package/lib/svgo/jsAPI.d.ts +2 -0
- package/lib/svgo/jsAPI.js +443 -0
- package/lib/svgo/plugins.js +109 -0
- package/lib/svgo/tools.js +137 -0
- package/lib/svgo-node.js +106 -0
- package/lib/svgo.js +83 -0
- package/lib/types.ts +172 -0
- package/lib/xast.js +102 -0
- package/package.json +130 -0
- package/plugins/_applyTransforms.js +335 -0
- package/plugins/_collections.js +2168 -0
- package/plugins/_path.js +816 -0
- package/plugins/_transforms.js +379 -0
- package/plugins/addAttributesToSVGElement.js +87 -0
- package/plugins/addClassesToSVGElement.js +87 -0
- package/plugins/cleanupAttrs.js +55 -0
- package/plugins/cleanupEnableBackground.js +75 -0
- package/plugins/cleanupIDs.js +297 -0
- package/plugins/cleanupListOfValues.js +154 -0
- package/plugins/cleanupNumericValues.js +113 -0
- package/plugins/collapseGroups.js +135 -0
- package/plugins/convertColors.js +152 -0
- package/plugins/convertEllipseToCircle.js +39 -0
- package/plugins/convertPathData.js +1023 -0
- package/plugins/convertShapeToPath.js +175 -0
- package/plugins/convertStyleToAttrs.js +132 -0
- package/plugins/convertTransform.js +432 -0
- package/plugins/inlineStyles.js +379 -0
- package/plugins/mergePaths.js +104 -0
- package/plugins/mergeStyles.js +93 -0
- package/plugins/minifyStyles.js +148 -0
- package/plugins/moveElemsAttrsToGroup.js +130 -0
- package/plugins/moveGroupAttrsToElems.js +62 -0
- package/plugins/plugins.js +56 -0
- package/plugins/prefixIds.js +241 -0
- package/plugins/preset-default.js +80 -0
- package/plugins/removeAttributesBySelector.js +99 -0
- package/plugins/removeAttrs.js +159 -0
- package/plugins/removeComments.js +31 -0
- package/plugins/removeDesc.js +41 -0
- package/plugins/removeDimensions.js +43 -0
- package/plugins/removeDoctype.js +42 -0
- package/plugins/removeEditorsNSData.js +68 -0
- package/plugins/removeElementsByAttr.js +78 -0
- package/plugins/removeEmptyAttrs.js +33 -0
- package/plugins/removeEmptyContainers.js +58 -0
- package/plugins/removeEmptyText.js +57 -0
- package/plugins/removeHiddenElems.js +318 -0
- package/plugins/removeMetadata.js +29 -0
- package/plugins/removeNonInheritableGroupAttrs.js +38 -0
- package/plugins/removeOffCanvasPaths.js +138 -0
- package/plugins/removeRasterImages.js +33 -0
- package/plugins/removeScriptElement.js +29 -0
- package/plugins/removeStyleElement.js +29 -0
- package/plugins/removeTitle.js +29 -0
- package/plugins/removeUnknownsAndDefaults.js +218 -0
- package/plugins/removeUnusedNS.js +61 -0
- package/plugins/removeUselessDefs.js +65 -0
- package/plugins/removeUselessStrokeAndFill.js +144 -0
- package/plugins/removeViewBox.js +51 -0
- package/plugins/removeXMLNS.js +30 -0
- package/plugins/removeXMLProcInst.js +30 -0
- package/plugins/reusePaths.js +113 -0
- package/plugins/sortAttrs.js +113 -0
- package/plugins/sortDefsChildren.js +60 -0
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @typedef {import('../lib/types').XastElement} XastElement
|
|
5
|
+
* @typedef {import('../lib/types').XastParent} XastParent
|
|
6
|
+
* @typedef {import('../lib/types').XastNode} XastNode
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const JSAPI = require('../lib/svgo/jsAPI.js');
|
|
10
|
+
|
|
11
|
+
exports.type = 'visitor';
|
|
12
|
+
exports.name = 'reusePaths';
|
|
13
|
+
exports.active = false;
|
|
14
|
+
exports.description =
|
|
15
|
+
'Finds <path> elements with the same d, fill, and ' +
|
|
16
|
+
'stroke, and converts them to <use> elements ' +
|
|
17
|
+
'referencing a single <path> def.';
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Finds <path> elements with the same d, fill, and stroke, and converts them to
|
|
21
|
+
* <use> elements referencing a single <path> def.
|
|
22
|
+
*
|
|
23
|
+
* @author Jacob Howcroft
|
|
24
|
+
*
|
|
25
|
+
* @type {import('../lib/types').Plugin<void>}
|
|
26
|
+
*/
|
|
27
|
+
exports.fn = () => {
|
|
28
|
+
/**
|
|
29
|
+
* @type {Map<string, Array<XastElement>>}
|
|
30
|
+
*/
|
|
31
|
+
const paths = new Map();
|
|
32
|
+
|
|
33
|
+
return {
|
|
34
|
+
element: {
|
|
35
|
+
enter: (node) => {
|
|
36
|
+
if (node.name === 'path' && node.attributes.d != null) {
|
|
37
|
+
const d = node.attributes.d;
|
|
38
|
+
const fill = node.attributes.fill || '';
|
|
39
|
+
const stroke = node.attributes.stroke || '';
|
|
40
|
+
const key = d + ';s:' + stroke + ';f:' + fill;
|
|
41
|
+
let list = paths.get(key);
|
|
42
|
+
if (list == null) {
|
|
43
|
+
list = [];
|
|
44
|
+
paths.set(key, list);
|
|
45
|
+
}
|
|
46
|
+
list.push(node);
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
|
|
50
|
+
exit: (node, parentNode) => {
|
|
51
|
+
if (node.name === 'svg' && parentNode.type === 'root') {
|
|
52
|
+
/**
|
|
53
|
+
* @type {XastElement}
|
|
54
|
+
*/
|
|
55
|
+
const rawDefs = {
|
|
56
|
+
type: 'element',
|
|
57
|
+
name: 'defs',
|
|
58
|
+
attributes: {},
|
|
59
|
+
children: [],
|
|
60
|
+
};
|
|
61
|
+
/**
|
|
62
|
+
* @type {XastElement}
|
|
63
|
+
*/
|
|
64
|
+
const defsTag = new JSAPI(rawDefs, node);
|
|
65
|
+
let index = 0;
|
|
66
|
+
for (const list of paths.values()) {
|
|
67
|
+
if (list.length > 1) {
|
|
68
|
+
// add reusable path to defs
|
|
69
|
+
/**
|
|
70
|
+
* @type {XastElement}
|
|
71
|
+
*/
|
|
72
|
+
const rawPath = {
|
|
73
|
+
type: 'element',
|
|
74
|
+
name: 'path',
|
|
75
|
+
attributes: { ...list[0].attributes },
|
|
76
|
+
children: [],
|
|
77
|
+
};
|
|
78
|
+
delete rawPath.attributes.transform;
|
|
79
|
+
let id;
|
|
80
|
+
if (rawPath.attributes.id == null) {
|
|
81
|
+
id = 'reuse-' + index;
|
|
82
|
+
index += 1;
|
|
83
|
+
rawPath.attributes.id = id;
|
|
84
|
+
} else {
|
|
85
|
+
id = rawPath.attributes.id;
|
|
86
|
+
delete list[0].attributes.id;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* @type {XastElement}
|
|
90
|
+
*/
|
|
91
|
+
const reusablePath = new JSAPI(rawPath, defsTag);
|
|
92
|
+
defsTag.children.push(reusablePath);
|
|
93
|
+
// convert paths to <use>
|
|
94
|
+
for (const pathNode of list) {
|
|
95
|
+
pathNode.name = 'use';
|
|
96
|
+
pathNode.attributes['xlink:href'] = '#' + id;
|
|
97
|
+
delete pathNode.attributes.d;
|
|
98
|
+
delete pathNode.attributes.stroke;
|
|
99
|
+
delete pathNode.attributes.fill;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
if (defsTag.children.length !== 0) {
|
|
104
|
+
if (node.attributes['xmlns:xlink'] == null) {
|
|
105
|
+
node.attributes['xmlns:xlink'] = 'http://www.w3.org/1999/xlink';
|
|
106
|
+
}
|
|
107
|
+
node.children.unshift(defsTag);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
};
|
|
113
|
+
};
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
exports.type = 'visitor';
|
|
4
|
+
exports.name = 'sortAttrs';
|
|
5
|
+
exports.active = false;
|
|
6
|
+
exports.description = 'Sort element attributes for better compression';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Sort element attributes for better compression
|
|
10
|
+
*
|
|
11
|
+
* @author Nikolay Frantsev
|
|
12
|
+
*
|
|
13
|
+
* @type {import('../lib/types').Plugin<{
|
|
14
|
+
* order?: Array<string>
|
|
15
|
+
* xmlnsOrder?: 'front' | 'alphabetical'
|
|
16
|
+
* }>}
|
|
17
|
+
*/
|
|
18
|
+
exports.fn = (_root, params) => {
|
|
19
|
+
const {
|
|
20
|
+
order = [
|
|
21
|
+
'id',
|
|
22
|
+
'width',
|
|
23
|
+
'height',
|
|
24
|
+
'x',
|
|
25
|
+
'x1',
|
|
26
|
+
'x2',
|
|
27
|
+
'y',
|
|
28
|
+
'y1',
|
|
29
|
+
'y2',
|
|
30
|
+
'cx',
|
|
31
|
+
'cy',
|
|
32
|
+
'r',
|
|
33
|
+
'fill',
|
|
34
|
+
'stroke',
|
|
35
|
+
'marker',
|
|
36
|
+
'd',
|
|
37
|
+
'points',
|
|
38
|
+
],
|
|
39
|
+
xmlnsOrder = 'front',
|
|
40
|
+
} = params;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* @type {(name: string) => number}
|
|
44
|
+
*/
|
|
45
|
+
const getNsPriority = (name) => {
|
|
46
|
+
if (xmlnsOrder === 'front') {
|
|
47
|
+
// put xmlns first
|
|
48
|
+
if (name === 'xmlns') {
|
|
49
|
+
return 3;
|
|
50
|
+
}
|
|
51
|
+
// xmlns:* attributes second
|
|
52
|
+
if (name.startsWith('xmlns:')) {
|
|
53
|
+
return 2;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
// other namespaces after and sort them alphabetically
|
|
57
|
+
if (name.includes(':')) {
|
|
58
|
+
return 1;
|
|
59
|
+
}
|
|
60
|
+
// other attributes
|
|
61
|
+
return 0;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* @type {(a: [string, string], b: [string, string]) => number}
|
|
66
|
+
*/
|
|
67
|
+
const compareAttrs = ([aName], [bName]) => {
|
|
68
|
+
// sort namespaces
|
|
69
|
+
const aPriority = getNsPriority(aName);
|
|
70
|
+
const bPriority = getNsPriority(bName);
|
|
71
|
+
const priorityNs = bPriority - aPriority;
|
|
72
|
+
if (priorityNs !== 0) {
|
|
73
|
+
return priorityNs;
|
|
74
|
+
}
|
|
75
|
+
// extract the first part from attributes
|
|
76
|
+
// for example "fill" from "fill" and "fill-opacity"
|
|
77
|
+
const [aPart] = aName.split('-');
|
|
78
|
+
const [bPart] = bName.split('-');
|
|
79
|
+
// rely on alphabetical sort when the first part is the same
|
|
80
|
+
if (aPart !== bPart) {
|
|
81
|
+
const aInOrderFlag = order.includes(aPart) ? 1 : 0;
|
|
82
|
+
const bInOrderFlag = order.includes(bPart) ? 1 : 0;
|
|
83
|
+
// sort by position in order param
|
|
84
|
+
if (aInOrderFlag === 1 && bInOrderFlag === 1) {
|
|
85
|
+
return order.indexOf(aPart) - order.indexOf(bPart);
|
|
86
|
+
}
|
|
87
|
+
// put attributes from order param before others
|
|
88
|
+
const priorityOrder = bInOrderFlag - aInOrderFlag;
|
|
89
|
+
if (priorityOrder !== 0) {
|
|
90
|
+
return priorityOrder;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
// sort alphabetically
|
|
94
|
+
return aName < bName ? -1 : 1;
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
return {
|
|
98
|
+
element: {
|
|
99
|
+
enter: (node) => {
|
|
100
|
+
const attrs = Object.entries(node.attributes);
|
|
101
|
+
attrs.sort(compareAttrs);
|
|
102
|
+
/**
|
|
103
|
+
* @type {Record<string, string>}
|
|
104
|
+
*/
|
|
105
|
+
const sortedAttributes = {};
|
|
106
|
+
for (const [name, value] of attrs) {
|
|
107
|
+
sortedAttributes[name] = value;
|
|
108
|
+
}
|
|
109
|
+
node.attributes = sortedAttributes;
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
};
|
|
113
|
+
};
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
exports.type = 'visitor';
|
|
4
|
+
exports.name = 'sortDefsChildren';
|
|
5
|
+
exports.active = true;
|
|
6
|
+
exports.description = 'Sorts children of <defs> to improve compression';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Sorts children of defs in order to improve compression.
|
|
10
|
+
* Sorted first by frequency then by element name length then by element name (to ensure grouping).
|
|
11
|
+
*
|
|
12
|
+
* @author David Leston
|
|
13
|
+
*
|
|
14
|
+
* @type {import('../lib/types').Plugin<void>}
|
|
15
|
+
*/
|
|
16
|
+
exports.fn = () => {
|
|
17
|
+
return {
|
|
18
|
+
element: {
|
|
19
|
+
enter: (node) => {
|
|
20
|
+
if (node.name === 'defs') {
|
|
21
|
+
/**
|
|
22
|
+
* @type {Map<string, number>}
|
|
23
|
+
*/
|
|
24
|
+
const frequencies = new Map();
|
|
25
|
+
for (const child of node.children) {
|
|
26
|
+
if (child.type === 'element') {
|
|
27
|
+
const frequency = frequencies.get(child.name);
|
|
28
|
+
if (frequency == null) {
|
|
29
|
+
frequencies.set(child.name, 1);
|
|
30
|
+
} else {
|
|
31
|
+
frequencies.set(child.name, frequency + 1);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
node.children.sort((a, b) => {
|
|
36
|
+
if (a.type !== 'element' || b.type !== 'element') {
|
|
37
|
+
return 0;
|
|
38
|
+
}
|
|
39
|
+
const aFrequency = frequencies.get(a.name);
|
|
40
|
+
const bFrequency = frequencies.get(b.name);
|
|
41
|
+
if (aFrequency != null && bFrequency != null) {
|
|
42
|
+
const frequencyComparison = bFrequency - aFrequency;
|
|
43
|
+
if (frequencyComparison !== 0) {
|
|
44
|
+
return frequencyComparison;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
const lengthComparison = b.name.length - a.name.length;
|
|
48
|
+
if (lengthComparison !== 0) {
|
|
49
|
+
return lengthComparison;
|
|
50
|
+
}
|
|
51
|
+
if (a.name !== b.name) {
|
|
52
|
+
return a.name > b.name ? -1 : 1;
|
|
53
|
+
}
|
|
54
|
+
return 0;
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
};
|