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.
Files changed (80) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +294 -0
  3. package/bin/svgo +10 -0
  4. package/dist/svgo.browser.js +1 -0
  5. package/lib/css-tools.js +239 -0
  6. package/lib/parser.js +259 -0
  7. package/lib/path.js +347 -0
  8. package/lib/stringifier.js +326 -0
  9. package/lib/style.js +283 -0
  10. package/lib/svgo/coa.js +517 -0
  11. package/lib/svgo/config.js +138 -0
  12. package/lib/svgo/css-class-list.js +72 -0
  13. package/lib/svgo/css-select-adapter.d.ts +2 -0
  14. package/lib/svgo/css-select-adapter.js +120 -0
  15. package/lib/svgo/css-style-declaration.js +232 -0
  16. package/lib/svgo/jsAPI.d.ts +2 -0
  17. package/lib/svgo/jsAPI.js +443 -0
  18. package/lib/svgo/plugins.js +109 -0
  19. package/lib/svgo/tools.js +137 -0
  20. package/lib/svgo-node.js +106 -0
  21. package/lib/svgo.js +83 -0
  22. package/lib/types.ts +172 -0
  23. package/lib/xast.js +102 -0
  24. package/package.json +130 -0
  25. package/plugins/_applyTransforms.js +335 -0
  26. package/plugins/_collections.js +2168 -0
  27. package/plugins/_path.js +816 -0
  28. package/plugins/_transforms.js +379 -0
  29. package/plugins/addAttributesToSVGElement.js +87 -0
  30. package/plugins/addClassesToSVGElement.js +87 -0
  31. package/plugins/cleanupAttrs.js +55 -0
  32. package/plugins/cleanupEnableBackground.js +75 -0
  33. package/plugins/cleanupIDs.js +297 -0
  34. package/plugins/cleanupListOfValues.js +154 -0
  35. package/plugins/cleanupNumericValues.js +113 -0
  36. package/plugins/collapseGroups.js +135 -0
  37. package/plugins/convertColors.js +152 -0
  38. package/plugins/convertEllipseToCircle.js +39 -0
  39. package/plugins/convertPathData.js +1023 -0
  40. package/plugins/convertShapeToPath.js +175 -0
  41. package/plugins/convertStyleToAttrs.js +132 -0
  42. package/plugins/convertTransform.js +432 -0
  43. package/plugins/inlineStyles.js +379 -0
  44. package/plugins/mergePaths.js +104 -0
  45. package/plugins/mergeStyles.js +93 -0
  46. package/plugins/minifyStyles.js +148 -0
  47. package/plugins/moveElemsAttrsToGroup.js +130 -0
  48. package/plugins/moveGroupAttrsToElems.js +62 -0
  49. package/plugins/plugins.js +56 -0
  50. package/plugins/prefixIds.js +241 -0
  51. package/plugins/preset-default.js +80 -0
  52. package/plugins/removeAttributesBySelector.js +99 -0
  53. package/plugins/removeAttrs.js +159 -0
  54. package/plugins/removeComments.js +31 -0
  55. package/plugins/removeDesc.js +41 -0
  56. package/plugins/removeDimensions.js +43 -0
  57. package/plugins/removeDoctype.js +42 -0
  58. package/plugins/removeEditorsNSData.js +68 -0
  59. package/plugins/removeElementsByAttr.js +78 -0
  60. package/plugins/removeEmptyAttrs.js +33 -0
  61. package/plugins/removeEmptyContainers.js +58 -0
  62. package/plugins/removeEmptyText.js +57 -0
  63. package/plugins/removeHiddenElems.js +318 -0
  64. package/plugins/removeMetadata.js +29 -0
  65. package/plugins/removeNonInheritableGroupAttrs.js +38 -0
  66. package/plugins/removeOffCanvasPaths.js +138 -0
  67. package/plugins/removeRasterImages.js +33 -0
  68. package/plugins/removeScriptElement.js +29 -0
  69. package/plugins/removeStyleElement.js +29 -0
  70. package/plugins/removeTitle.js +29 -0
  71. package/plugins/removeUnknownsAndDefaults.js +218 -0
  72. package/plugins/removeUnusedNS.js +61 -0
  73. package/plugins/removeUselessDefs.js +65 -0
  74. package/plugins/removeUselessStrokeAndFill.js +144 -0
  75. package/plugins/removeViewBox.js +51 -0
  76. package/plugins/removeXMLNS.js +30 -0
  77. package/plugins/removeXMLProcInst.js +30 -0
  78. package/plugins/reusePaths.js +113 -0
  79. package/plugins/sortAttrs.js +113 -0
  80. 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
+ };