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,175 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * @typedef {import('../lib/types').PathDataItem} PathDataItem
5
+ */
6
+
7
+ const { stringifyPathData } = require('../lib/path.js');
8
+ const { detachNodeFromParent } = require('../lib/xast.js');
9
+
10
+ exports.name = 'convertShapeToPath';
11
+ exports.type = 'visitor';
12
+ exports.active = true;
13
+ exports.description = 'converts basic shapes to more compact path form';
14
+
15
+ const regNumber = /[-+]?(?:\d*\.\d+|\d+\.?)(?:[eE][-+]?\d+)?/g;
16
+
17
+ /**
18
+ * Converts basic shape to more compact path.
19
+ * It also allows further optimizations like
20
+ * combining paths with similar attributes.
21
+ *
22
+ * @see https://www.w3.org/TR/SVG11/shapes.html
23
+ *
24
+ * @author Lev Solntsev
25
+ *
26
+ * @type {import('../lib/types').Plugin<{
27
+ * convertArcs?: boolean,
28
+ * floatPrecision?: number
29
+ * }>}
30
+ */
31
+ exports.fn = (root, params) => {
32
+ const { convertArcs = false, floatPrecision: precision } = params;
33
+
34
+ return {
35
+ element: {
36
+ enter: (node, parentNode) => {
37
+ // convert rect to path
38
+ if (
39
+ node.name === 'rect' &&
40
+ node.attributes.width != null &&
41
+ node.attributes.height != null &&
42
+ node.attributes.rx == null &&
43
+ node.attributes.ry == null
44
+ ) {
45
+ const x = Number(node.attributes.x || '0');
46
+ const y = Number(node.attributes.y || '0');
47
+ const width = Number(node.attributes.width);
48
+ const height = Number(node.attributes.height);
49
+ // Values like '100%' compute to NaN, thus running after
50
+ // cleanupNumericValues when 'px' units has already been removed.
51
+ // TODO: Calculate sizes from % and non-px units if possible.
52
+ if (Number.isNaN(x - y + width - height)) return;
53
+ /**
54
+ * @type {Array<PathDataItem>}
55
+ */
56
+ const pathData = [
57
+ { command: 'M', args: [x, y] },
58
+ { command: 'H', args: [x + width] },
59
+ { command: 'V', args: [y + height] },
60
+ { command: 'H', args: [x] },
61
+ { command: 'z', args: [] },
62
+ ];
63
+ node.name = 'path';
64
+ node.attributes.d = stringifyPathData({ pathData, precision });
65
+ delete node.attributes.x;
66
+ delete node.attributes.y;
67
+ delete node.attributes.width;
68
+ delete node.attributes.height;
69
+ }
70
+
71
+ // convert line to path
72
+ if (node.name === 'line') {
73
+ const x1 = Number(node.attributes.x1 || '0');
74
+ const y1 = Number(node.attributes.y1 || '0');
75
+ const x2 = Number(node.attributes.x2 || '0');
76
+ const y2 = Number(node.attributes.y2 || '0');
77
+ if (Number.isNaN(x1 - y1 + x2 - y2)) return;
78
+ /**
79
+ * @type {Array<PathDataItem>}
80
+ */
81
+ const pathData = [
82
+ { command: 'M', args: [x1, y1] },
83
+ { command: 'L', args: [x2, y2] },
84
+ ];
85
+ node.name = 'path';
86
+ node.attributes.d = stringifyPathData({ pathData, precision });
87
+ delete node.attributes.x1;
88
+ delete node.attributes.y1;
89
+ delete node.attributes.x2;
90
+ delete node.attributes.y2;
91
+ }
92
+
93
+ // convert polyline and polygon to path
94
+ if (
95
+ (node.name === 'polyline' || node.name === 'polygon') &&
96
+ node.attributes.points != null
97
+ ) {
98
+ const coords = (node.attributes.points.match(regNumber) || []).map(
99
+ Number
100
+ );
101
+ if (coords.length < 4) {
102
+ detachNodeFromParent(node, parentNode);
103
+ return;
104
+ }
105
+ /**
106
+ * @type {Array<PathDataItem>}
107
+ */
108
+ const pathData = [];
109
+ for (let i = 0; i < coords.length; i += 2) {
110
+ pathData.push({
111
+ command: i === 0 ? 'M' : 'L',
112
+ args: coords.slice(i, i + 2),
113
+ });
114
+ }
115
+ if (node.name === 'polygon') {
116
+ pathData.push({ command: 'z', args: [] });
117
+ }
118
+ node.name = 'path';
119
+ node.attributes.d = stringifyPathData({ pathData, precision });
120
+ delete node.attributes.points;
121
+ }
122
+
123
+ // optionally convert circle
124
+ if (node.name === 'circle' && convertArcs) {
125
+ const cx = Number(node.attributes.cx || '0');
126
+ const cy = Number(node.attributes.cy || '0');
127
+ const r = Number(node.attributes.r || '0');
128
+ if (Number.isNaN(cx - cy + r)) {
129
+ return;
130
+ }
131
+ /**
132
+ * @type {Array<PathDataItem>}
133
+ */
134
+ const pathData = [
135
+ { command: 'M', args: [cx, cy - r] },
136
+ { command: 'A', args: [r, r, 0, 1, 0, cx, cy + r] },
137
+ { command: 'A', args: [r, r, 0, 1, 0, cx, cy - r] },
138
+ { command: 'z', args: [] },
139
+ ];
140
+ node.name = 'path';
141
+ node.attributes.d = stringifyPathData({ pathData, precision });
142
+ delete node.attributes.cx;
143
+ delete node.attributes.cy;
144
+ delete node.attributes.r;
145
+ }
146
+
147
+ // optionally covert ellipse
148
+ if (node.name === 'ellipse' && convertArcs) {
149
+ const ecx = Number(node.attributes.cx || '0');
150
+ const ecy = Number(node.attributes.cy || '0');
151
+ const rx = Number(node.attributes.rx || '0');
152
+ const ry = Number(node.attributes.ry || '0');
153
+ if (Number.isNaN(ecx - ecy + rx - ry)) {
154
+ return;
155
+ }
156
+ /**
157
+ * @type {Array<PathDataItem>}
158
+ */
159
+ const pathData = [
160
+ { command: 'M', args: [ecx, ecy - ry] },
161
+ { command: 'A', args: [rx, ry, 0, 1, 0, ecx, ecy + ry] },
162
+ { command: 'A', args: [rx, ry, 0, 1, 0, ecx, ecy - ry] },
163
+ { command: 'z', args: [] },
164
+ ];
165
+ node.name = 'path';
166
+ node.attributes.d = stringifyPathData({ pathData, precision });
167
+ delete node.attributes.cx;
168
+ delete node.attributes.cy;
169
+ delete node.attributes.rx;
170
+ delete node.attributes.ry;
171
+ }
172
+ },
173
+ },
174
+ };
175
+ };
@@ -0,0 +1,132 @@
1
+ 'use strict';
2
+
3
+ exports.name = 'convertStyleToAttrs';
4
+
5
+ exports.type = 'perItem';
6
+
7
+ exports.active = false;
8
+
9
+ exports.description = 'converts style to attributes';
10
+
11
+ exports.params = {
12
+ keepImportant: false,
13
+ };
14
+
15
+ var stylingProps = require('./_collections').attrsGroups.presentation,
16
+ rEscape = '\\\\(?:[0-9a-f]{1,6}\\s?|\\r\\n|.)', // Like \" or \2051. Code points consume one space.
17
+ rAttr = '\\s*(' + g('[^:;\\\\]', rEscape) + '*?)\\s*', // attribute name like ‘fill’
18
+ rSingleQuotes = "'(?:[^'\\n\\r\\\\]|" + rEscape + ")*?(?:'|$)", // string in single quotes: 'smth'
19
+ rQuotes = '"(?:[^"\\n\\r\\\\]|' + rEscape + ')*?(?:"|$)', // string in double quotes: "smth"
20
+ rQuotedString = new RegExp('^' + g(rSingleQuotes, rQuotes) + '$'),
21
+ // Parentheses, E.g.: url(data:image/png;base64,iVBO...).
22
+ // ':' and ';' inside of it should be threated as is. (Just like in strings.)
23
+ rParenthesis =
24
+ '\\(' + g('[^\'"()\\\\]+', rEscape, rSingleQuotes, rQuotes) + '*?' + '\\)',
25
+ // The value. It can have strings and parentheses (see above). Fallbacks to anything in case of unexpected input.
26
+ rValue =
27
+ '\\s*(' +
28
+ g(
29
+ '[^!\'"();\\\\]+?',
30
+ rEscape,
31
+ rSingleQuotes,
32
+ rQuotes,
33
+ rParenthesis,
34
+ '[^;]*?'
35
+ ) +
36
+ '*?' +
37
+ ')',
38
+ // End of declaration. Spaces outside of capturing groups help to do natural trimming.
39
+ rDeclEnd = '\\s*(?:;\\s*|$)',
40
+ // Important rule
41
+ rImportant = '(\\s*!important(?![-(\\w]))?',
42
+ // Final RegExp to parse CSS declarations.
43
+ regDeclarationBlock = new RegExp(
44
+ rAttr + ':' + rValue + rImportant + rDeclEnd,
45
+ 'ig'
46
+ ),
47
+ // Comments expression. Honors escape sequences and strings.
48
+ regStripComments = new RegExp(
49
+ g(rEscape, rSingleQuotes, rQuotes, '/\\*[^]*?\\*/'),
50
+ 'ig'
51
+ );
52
+
53
+ /**
54
+ * Convert style in attributes. Cleanups comments and illegal declarations (without colon) as a side effect.
55
+ *
56
+ * @example
57
+ * <g style="fill:#000; color: #fff;">
58
+ * ⬇
59
+ * <g fill="#000" color="#fff">
60
+ *
61
+ * @example
62
+ * <g style="fill:#000; color: #fff; -webkit-blah: blah">
63
+ * ⬇
64
+ * <g fill="#000" color="#fff" style="-webkit-blah: blah">
65
+ *
66
+ * @param {Object} item current iteration item
67
+ * @return {Boolean} if false, item will be filtered out
68
+ *
69
+ * @author Kir Belevich
70
+ */
71
+ exports.fn = function (item, params) {
72
+ if (item.type === 'element' && item.attributes.style != null) {
73
+ // ['opacity: 1', 'color: #000']
74
+ let styles = [];
75
+ const newAttributes = {};
76
+
77
+ // Strip CSS comments preserving escape sequences and strings.
78
+ const styleValue = item.attributes.style.replace(
79
+ regStripComments,
80
+ (match) => {
81
+ return match[0] == '/'
82
+ ? ''
83
+ : match[0] == '\\' && /[-g-z]/i.test(match[1])
84
+ ? match[1]
85
+ : match;
86
+ }
87
+ );
88
+
89
+ regDeclarationBlock.lastIndex = 0;
90
+ // eslint-disable-next-line no-cond-assign
91
+ for (var rule; (rule = regDeclarationBlock.exec(styleValue)); ) {
92
+ if (!params.keepImportant || !rule[3]) {
93
+ styles.push([rule[1], rule[2]]);
94
+ }
95
+ }
96
+
97
+ if (styles.length) {
98
+ styles = styles.filter(function (style) {
99
+ if (style[0]) {
100
+ var prop = style[0].toLowerCase(),
101
+ val = style[1];
102
+
103
+ if (rQuotedString.test(val)) {
104
+ val = val.slice(1, -1);
105
+ }
106
+
107
+ if (stylingProps.includes(prop)) {
108
+ newAttributes[prop] = val;
109
+
110
+ return false;
111
+ }
112
+ }
113
+
114
+ return true;
115
+ });
116
+
117
+ Object.assign(item.attributes, newAttributes);
118
+
119
+ if (styles.length) {
120
+ item.attributes.style = styles
121
+ .map((declaration) => declaration.join(':'))
122
+ .join(';');
123
+ } else {
124
+ delete item.attributes.style;
125
+ }
126
+ }
127
+ }
128
+ };
129
+
130
+ function g() {
131
+ return '(?:' + Array.prototype.join.call(arguments, '|') + ')';
132
+ }