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
package/lib/style.js ADDED
@@ -0,0 +1,283 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * @typedef {import('css-tree-v2').Rule} CsstreeRule
5
+ * @typedef {import('./types').Specificity} Specificity
6
+ * @typedef {import('./types').Stylesheet} Stylesheet
7
+ * @typedef {import('./types').StylesheetRule} StylesheetRule
8
+ * @typedef {import('./types').StylesheetDeclaration} StylesheetDeclaration
9
+ * @typedef {import('./types').ComputedStyles} ComputedStyles
10
+ * @typedef {import('./types').XastRoot} XastRoot
11
+ * @typedef {import('./types').XastElement} XastElement
12
+ * @typedef {import('./types').XastParent} XastParent
13
+ * @typedef {import('./types').XastChild} XastChild
14
+ */
15
+
16
+ const stable = require('stable');
17
+ const csstree = require('css-tree-v2');
18
+ // @ts-ignore not defined in @types/csso
19
+ const specificity = require('csso/lib/restructure/prepare/specificity');
20
+ const { visit, matches } = require('./xast.js');
21
+ const {
22
+ attrsGroups,
23
+ inheritableAttrs,
24
+ presentationNonInheritableGroupAttrs,
25
+ } = require('../plugins/_collections.js');
26
+
27
+ // @ts-ignore not defined in @types/csstree
28
+ const csstreeWalkSkip = csstree.walk.skip;
29
+
30
+ /**
31
+ * @type {(ruleNode: CsstreeRule, dynamic: boolean) => StylesheetRule}
32
+ */
33
+ const parseRule = (ruleNode, dynamic) => {
34
+ let selectors;
35
+ let selectorsSpecificity;
36
+ /**
37
+ * @type {Array<StylesheetDeclaration>}
38
+ */
39
+ const declarations = [];
40
+ csstree.walk(ruleNode, (cssNode) => {
41
+ if (cssNode.type === 'SelectorList') {
42
+ // compute specificity from original node to consider pseudo classes
43
+ selectorsSpecificity = specificity(cssNode);
44
+ const newSelectorsNode = csstree.clone(cssNode);
45
+ csstree.walk(newSelectorsNode, (pseudoClassNode, item, list) => {
46
+ if (pseudoClassNode.type === 'PseudoClassSelector') {
47
+ dynamic = true;
48
+ list.remove(item);
49
+ }
50
+ });
51
+ selectors = csstree.generate(newSelectorsNode);
52
+ return csstreeWalkSkip;
53
+ }
54
+ if (cssNode.type === 'Declaration') {
55
+ declarations.push({
56
+ name: cssNode.property,
57
+ value: csstree.generate(cssNode.value),
58
+ important: cssNode.important === true,
59
+ });
60
+ return csstreeWalkSkip;
61
+ }
62
+ });
63
+ if (selectors == null || selectorsSpecificity == null) {
64
+ throw Error('assert');
65
+ }
66
+ return {
67
+ dynamic,
68
+ selectors,
69
+ specificity: selectorsSpecificity,
70
+ declarations,
71
+ };
72
+ };
73
+
74
+ /**
75
+ * @type {(css: string, dynamic: boolean) => Array<StylesheetRule>}
76
+ */
77
+ const parseStylesheet = (css, dynamic) => {
78
+ /**
79
+ * @type {Array<StylesheetRule>}
80
+ */
81
+ const rules = [];
82
+ const ast = csstree.parse(css, {
83
+ parseValue: false,
84
+ parseAtrulePrelude: false,
85
+ });
86
+ csstree.walk(ast, (cssNode) => {
87
+ if (cssNode.type === 'Rule') {
88
+ rules.push(parseRule(cssNode, dynamic || false));
89
+ return csstreeWalkSkip;
90
+ }
91
+ if (cssNode.type === 'Atrule') {
92
+ if (cssNode.name === 'keyframes') {
93
+ return csstreeWalkSkip;
94
+ }
95
+ csstree.walk(cssNode, (ruleNode) => {
96
+ if (ruleNode.type === 'Rule') {
97
+ rules.push(parseRule(ruleNode, dynamic || true));
98
+ return csstreeWalkSkip;
99
+ }
100
+ });
101
+ return csstreeWalkSkip;
102
+ }
103
+ });
104
+ return rules;
105
+ };
106
+
107
+ /**
108
+ * @type {(css: string) => Array<StylesheetDeclaration>}
109
+ */
110
+ const parseStyleDeclarations = (css) => {
111
+ /**
112
+ * @type {Array<StylesheetDeclaration>}
113
+ */
114
+ const declarations = [];
115
+ const ast = csstree.parse(css, {
116
+ context: 'declarationList',
117
+ parseValue: false,
118
+ });
119
+ csstree.walk(ast, (cssNode) => {
120
+ if (cssNode.type === 'Declaration') {
121
+ declarations.push({
122
+ name: cssNode.property,
123
+ value: csstree.generate(cssNode.value),
124
+ important: cssNode.important === true,
125
+ });
126
+ }
127
+ });
128
+ return declarations;
129
+ };
130
+
131
+ /**
132
+ * @type {(stylesheet: Stylesheet, node: XastElement) => ComputedStyles}
133
+ */
134
+ const computeOwnStyle = (stylesheet, node) => {
135
+ /**
136
+ * @type {ComputedStyles}
137
+ */
138
+ const computedStyle = {};
139
+ const importantStyles = new Map();
140
+
141
+ // collect attributes
142
+ for (const [name, value] of Object.entries(node.attributes)) {
143
+ if (attrsGroups.presentation.includes(name)) {
144
+ computedStyle[name] = { type: 'static', inherited: false, value };
145
+ importantStyles.set(name, false);
146
+ }
147
+ }
148
+
149
+ // collect matching rules
150
+ for (const { selectors, declarations, dynamic } of stylesheet.rules) {
151
+ if (matches(node, selectors)) {
152
+ for (const { name, value, important } of declarations) {
153
+ const computed = computedStyle[name];
154
+ if (computed && computed.type === 'dynamic') {
155
+ continue;
156
+ }
157
+ if (dynamic) {
158
+ computedStyle[name] = { type: 'dynamic', inherited: false };
159
+ continue;
160
+ }
161
+ if (
162
+ computed == null ||
163
+ important === true ||
164
+ importantStyles.get(name) === false
165
+ ) {
166
+ computedStyle[name] = { type: 'static', inherited: false, value };
167
+ importantStyles.set(name, important);
168
+ }
169
+ }
170
+ }
171
+ }
172
+
173
+ // collect inline styles
174
+ const styleDeclarations =
175
+ node.attributes.style == null
176
+ ? []
177
+ : parseStyleDeclarations(node.attributes.style);
178
+ for (const { name, value, important } of styleDeclarations) {
179
+ const computed = computedStyle[name];
180
+ if (computed && computed.type === 'dynamic') {
181
+ continue;
182
+ }
183
+ if (
184
+ computed == null ||
185
+ important === true ||
186
+ importantStyles.get(name) === false
187
+ ) {
188
+ computedStyle[name] = { type: 'static', inherited: false, value };
189
+ importantStyles.set(name, important);
190
+ }
191
+ }
192
+
193
+ return computedStyle;
194
+ };
195
+
196
+ /**
197
+ * Compares two selector specificities.
198
+ * extracted from https://github.com/keeganstreet/specificity/blob/master/specificity.js#L211
199
+ *
200
+ * @type {(a: Specificity, b: Specificity) => number}
201
+ */
202
+ const compareSpecificity = (a, b) => {
203
+ for (var i = 0; i < 4; i += 1) {
204
+ if (a[i] < b[i]) {
205
+ return -1;
206
+ } else if (a[i] > b[i]) {
207
+ return 1;
208
+ }
209
+ }
210
+
211
+ return 0;
212
+ };
213
+
214
+ /**
215
+ * @type {(root: XastRoot) => Stylesheet}
216
+ */
217
+ const collectStylesheet = (root) => {
218
+ /**
219
+ * @type {Array<StylesheetRule>}
220
+ */
221
+ const rules = [];
222
+ /**
223
+ * @type {Map<XastElement, XastParent>}
224
+ */
225
+ const parents = new Map();
226
+ visit(root, {
227
+ element: {
228
+ enter: (node, parentNode) => {
229
+ // store parents
230
+ parents.set(node, parentNode);
231
+ // find and parse all styles
232
+ if (node.name === 'style') {
233
+ const dynamic =
234
+ node.attributes.media != null && node.attributes.media !== 'all';
235
+ if (
236
+ node.attributes.type == null ||
237
+ node.attributes.type === '' ||
238
+ node.attributes.type === 'text/css'
239
+ ) {
240
+ const children = node.children;
241
+ for (const child of children) {
242
+ if (child.type === 'text' || child.type === 'cdata') {
243
+ rules.push(...parseStylesheet(child.value, dynamic));
244
+ }
245
+ }
246
+ }
247
+ }
248
+ },
249
+ },
250
+ });
251
+ // sort by selectors specificity
252
+ stable.inplace(rules, (a, b) =>
253
+ compareSpecificity(a.specificity, b.specificity)
254
+ );
255
+ return { rules, parents };
256
+ };
257
+ exports.collectStylesheet = collectStylesheet;
258
+
259
+ /**
260
+ * @type {(stylesheet: Stylesheet, node: XastElement) => ComputedStyles}
261
+ */
262
+ const computeStyle = (stylesheet, node) => {
263
+ const { parents } = stylesheet;
264
+ // collect inherited styles
265
+ const computedStyles = computeOwnStyle(stylesheet, node);
266
+ let parent = parents.get(node);
267
+ while (parent != null && parent.type !== 'root') {
268
+ const inheritedStyles = computeOwnStyle(stylesheet, parent);
269
+ for (const [name, computed] of Object.entries(inheritedStyles)) {
270
+ if (
271
+ computedStyles[name] == null &&
272
+ // ignore not inheritable styles
273
+ inheritableAttrs.includes(name) === true &&
274
+ presentationNonInheritableGroupAttrs.includes(name) === false
275
+ ) {
276
+ computedStyles[name] = { ...computed, inherited: true };
277
+ }
278
+ }
279
+ parent = parents.get(parent);
280
+ }
281
+ return computedStyles;
282
+ };
283
+ exports.computeStyle = computeStyle;