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,29 @@
1
+ 'use strict';
2
+
3
+ const { detachNodeFromParent } = require('../lib/xast.js');
4
+
5
+ exports.name = 'removeStyleElement';
6
+ exports.type = 'visitor';
7
+ exports.active = false;
8
+ exports.description = 'removes <style> element (disabled by default)';
9
+
10
+ /**
11
+ * Remove <style>.
12
+ *
13
+ * https://www.w3.org/TR/SVG11/styling.html#StyleElement
14
+ *
15
+ * @author Betsy Dupuis
16
+ *
17
+ * @type {import('../lib/types').Plugin<void>}
18
+ */
19
+ exports.fn = () => {
20
+ return {
21
+ element: {
22
+ enter: (node, parentNode) => {
23
+ if (node.name === 'style') {
24
+ detachNodeFromParent(node, parentNode);
25
+ }
26
+ },
27
+ },
28
+ };
29
+ };
@@ -0,0 +1,29 @@
1
+ 'use strict';
2
+
3
+ const { detachNodeFromParent } = require('../lib/xast.js');
4
+
5
+ exports.name = 'removeTitle';
6
+ exports.type = 'visitor';
7
+ exports.active = true;
8
+ exports.description = 'removes <title>';
9
+
10
+ /**
11
+ * Remove <title>.
12
+ *
13
+ * https://developer.mozilla.org/en-US/docs/Web/SVG/Element/title
14
+ *
15
+ * @author Igor Kalashnikov
16
+ *
17
+ * @type {import('../lib/types').Plugin<void>}
18
+ */
19
+ exports.fn = () => {
20
+ return {
21
+ element: {
22
+ enter: (node, parentNode) => {
23
+ if (node.name === 'title') {
24
+ detachNodeFromParent(node, parentNode);
25
+ }
26
+ },
27
+ },
28
+ };
29
+ };
@@ -0,0 +1,218 @@
1
+ 'use strict';
2
+
3
+ const { visitSkip, detachNodeFromParent } = require('../lib/xast.js');
4
+ const { collectStylesheet, computeStyle } = require('../lib/style.js');
5
+ const {
6
+ elems,
7
+ attrsGroups,
8
+ elemsGroups,
9
+ attrsGroupsDefaults,
10
+ presentationNonInheritableGroupAttrs,
11
+ } = require('./_collections');
12
+
13
+ exports.type = 'visitor';
14
+ exports.name = 'removeUnknownsAndDefaults';
15
+ exports.active = true;
16
+ exports.description =
17
+ 'removes unknown elements content and attributes, removes attrs with default values';
18
+
19
+ // resolve all groups references
20
+
21
+ /**
22
+ * @type {Map<string, Set<string>>}
23
+ */
24
+ const allowedChildrenPerElement = new Map();
25
+ /**
26
+ * @type {Map<string, Set<string>>}
27
+ */
28
+ const allowedAttributesPerElement = new Map();
29
+ /**
30
+ * @type {Map<string, Map<string, string>>}
31
+ */
32
+ const attributesDefaultsPerElement = new Map();
33
+
34
+ for (const [name, config] of Object.entries(elems)) {
35
+ /**
36
+ * @type {Set<string>}
37
+ */
38
+ const allowedChildren = new Set();
39
+ if (config.content) {
40
+ for (const elementName of config.content) {
41
+ allowedChildren.add(elementName);
42
+ }
43
+ }
44
+ if (config.contentGroups) {
45
+ for (const contentGroupName of config.contentGroups) {
46
+ const elemsGroup = elemsGroups[contentGroupName];
47
+ if (elemsGroup) {
48
+ for (const elementName of elemsGroup) {
49
+ allowedChildren.add(elementName);
50
+ }
51
+ }
52
+ }
53
+ }
54
+ /**
55
+ * @type {Set<string>}
56
+ */
57
+ const allowedAttributes = new Set();
58
+ if (config.attrs) {
59
+ for (const attrName of config.attrs) {
60
+ allowedAttributes.add(attrName);
61
+ }
62
+ }
63
+ /**
64
+ * @type {Map<string, string>}
65
+ */
66
+ const attributesDefaults = new Map();
67
+ if (config.defaults) {
68
+ for (const [attrName, defaultValue] of Object.entries(config.defaults)) {
69
+ attributesDefaults.set(attrName, defaultValue);
70
+ }
71
+ }
72
+ for (const attrsGroupName of config.attrsGroups) {
73
+ const attrsGroup = attrsGroups[attrsGroupName];
74
+ if (attrsGroup) {
75
+ for (const attrName of attrsGroup) {
76
+ allowedAttributes.add(attrName);
77
+ }
78
+ }
79
+ const groupDefaults = attrsGroupsDefaults[attrsGroupName];
80
+ if (groupDefaults) {
81
+ for (const [attrName, defaultValue] of Object.entries(groupDefaults)) {
82
+ attributesDefaults.set(attrName, defaultValue);
83
+ }
84
+ }
85
+ }
86
+ allowedChildrenPerElement.set(name, allowedChildren);
87
+ allowedAttributesPerElement.set(name, allowedAttributes);
88
+ attributesDefaultsPerElement.set(name, attributesDefaults);
89
+ }
90
+
91
+ /**
92
+ * Remove unknown elements content and attributes,
93
+ * remove attributes with default values.
94
+ *
95
+ * @author Kir Belevich
96
+ *
97
+ * @type {import('../lib/types').Plugin<{
98
+ * unknownContent?: boolean,
99
+ * unknownAttrs?: boolean,
100
+ * defaultAttrs?: boolean,
101
+ * uselessOverrides?: boolean,
102
+ * keepDataAttrs?: boolean,
103
+ * keepAriaAttrs?: boolean,
104
+ * keepRoleAttr?: boolean,
105
+ * }>}
106
+ */
107
+ exports.fn = (root, params) => {
108
+ const {
109
+ unknownContent = true,
110
+ unknownAttrs = true,
111
+ defaultAttrs = true,
112
+ uselessOverrides = true,
113
+ keepDataAttrs = true,
114
+ keepAriaAttrs = true,
115
+ keepRoleAttr = false,
116
+ } = params;
117
+ const stylesheet = collectStylesheet(root);
118
+
119
+ return {
120
+ element: {
121
+ enter: (node, parentNode) => {
122
+ // skip namespaced elements
123
+ if (node.name.includes(':')) {
124
+ return;
125
+ }
126
+ // skip visiting foreignObject subtree
127
+ if (node.name === 'foreignObject') {
128
+ return visitSkip;
129
+ }
130
+
131
+ // remove unknown element's content
132
+ if (unknownContent && parentNode.type === 'element') {
133
+ const allowedChildren = allowedChildrenPerElement.get(
134
+ parentNode.name
135
+ );
136
+ if (allowedChildren == null || allowedChildren.size === 0) {
137
+ // remove unknown elements
138
+ if (allowedChildrenPerElement.get(node.name) == null) {
139
+ detachNodeFromParent(node, parentNode);
140
+ return;
141
+ }
142
+ } else {
143
+ // remove not allowed children
144
+ if (allowedChildren.has(node.name) === false) {
145
+ detachNodeFromParent(node, parentNode);
146
+ return;
147
+ }
148
+ }
149
+ }
150
+
151
+ const allowedAttributes = allowedAttributesPerElement.get(node.name);
152
+ const attributesDefaults = attributesDefaultsPerElement.get(node.name);
153
+ const computedParentStyle =
154
+ parentNode.type === 'element'
155
+ ? computeStyle(stylesheet, parentNode)
156
+ : null;
157
+
158
+ // remove element's unknown attrs and attrs with default values
159
+ for (const [name, value] of Object.entries(node.attributes)) {
160
+ if (keepDataAttrs && name.startsWith('data-')) {
161
+ continue;
162
+ }
163
+ if (keepAriaAttrs && name.startsWith('aria-')) {
164
+ continue;
165
+ }
166
+ if (keepRoleAttr && name === 'role') {
167
+ continue;
168
+ }
169
+ // skip xmlns attribute
170
+ if (name === 'xmlns') {
171
+ continue;
172
+ }
173
+ // skip namespaced attributes except xml:* and xlink:*
174
+ if (name.includes(':')) {
175
+ const [prefix] = name.split(':');
176
+ if (prefix !== 'xml' && prefix !== 'xlink') {
177
+ continue;
178
+ }
179
+ }
180
+
181
+ if (
182
+ unknownAttrs &&
183
+ allowedAttributes &&
184
+ allowedAttributes.has(name) === false
185
+ ) {
186
+ delete node.attributes[name];
187
+ }
188
+ if (
189
+ defaultAttrs &&
190
+ node.attributes.id == null &&
191
+ attributesDefaults &&
192
+ attributesDefaults.get(name) === value
193
+ ) {
194
+ // keep defaults if parent has own or inherited style
195
+ if (
196
+ computedParentStyle == null ||
197
+ computedParentStyle[name] == null
198
+ ) {
199
+ delete node.attributes[name];
200
+ }
201
+ }
202
+ if (uselessOverrides && node.attributes.id == null) {
203
+ const style =
204
+ computedParentStyle == null ? null : computedParentStyle[name];
205
+ if (
206
+ presentationNonInheritableGroupAttrs.includes(name) === false &&
207
+ style != null &&
208
+ style.type === 'static' &&
209
+ style.value === value
210
+ ) {
211
+ delete node.attributes[name];
212
+ }
213
+ }
214
+ }
215
+ },
216
+ },
217
+ };
218
+ };
@@ -0,0 +1,61 @@
1
+ 'use strict';
2
+
3
+ exports.type = 'visitor';
4
+ exports.name = 'removeUnusedNS';
5
+ exports.active = true;
6
+ exports.description = 'removes unused namespaces declaration';
7
+
8
+ /**
9
+ * Remove unused namespaces declaration from svg element
10
+ * which are not used in elements or attributes
11
+ *
12
+ * @author Kir Belevich
13
+ *
14
+ * @type {import('../lib/types').Plugin<void>}
15
+ */
16
+ exports.fn = () => {
17
+ /**
18
+ * @type {Set<string>}
19
+ */
20
+ const unusedNamespaces = new Set();
21
+ return {
22
+ element: {
23
+ enter: (node, parentNode) => {
24
+ // collect all namespaces from svg element
25
+ // (such as xmlns:xlink="http://www.w3.org/1999/xlink")
26
+ if (node.name === 'svg' && parentNode.type === 'root') {
27
+ for (const name of Object.keys(node.attributes)) {
28
+ if (name.startsWith('xmlns:')) {
29
+ const local = name.slice('xmlns:'.length);
30
+ unusedNamespaces.add(local);
31
+ }
32
+ }
33
+ }
34
+ if (unusedNamespaces.size !== 0) {
35
+ // preserve namespace used in nested elements names
36
+ if (node.name.includes(':')) {
37
+ const [ns] = node.name.split(':');
38
+ if (unusedNamespaces.has(ns)) {
39
+ unusedNamespaces.delete(ns);
40
+ }
41
+ }
42
+ // preserve namespace used in nested elements attributes
43
+ for (const name of Object.keys(node.attributes)) {
44
+ if (name.includes(':')) {
45
+ const [ns] = name.split(':');
46
+ unusedNamespaces.delete(ns);
47
+ }
48
+ }
49
+ }
50
+ },
51
+ exit: (node, parentNode) => {
52
+ // remove unused namespace attributes from svg element
53
+ if (node.name === 'svg' && parentNode.type === 'root') {
54
+ for (const name of unusedNamespaces) {
55
+ delete node.attributes[`xmlns:${name}`];
56
+ }
57
+ }
58
+ },
59
+ },
60
+ };
61
+ };
@@ -0,0 +1,65 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * @typedef {import('../lib/types').XastElement} XastElement
5
+ */
6
+
7
+ const { detachNodeFromParent } = require('../lib/xast.js');
8
+ const { elemsGroups } = require('./_collections.js');
9
+
10
+ exports.type = 'visitor';
11
+ exports.name = 'removeUselessDefs';
12
+ exports.active = true;
13
+ exports.description = 'removes elements in <defs> without id';
14
+
15
+ /**
16
+ * Removes content of defs and properties that aren't rendered directly without ids.
17
+ *
18
+ * @author Lev Solntsev
19
+ *
20
+ * @type {import('../lib/types').Plugin<void>}
21
+ */
22
+ exports.fn = () => {
23
+ return {
24
+ element: {
25
+ enter: (node, parentNode) => {
26
+ if (node.name === 'defs') {
27
+ /**
28
+ * @type {Array<XastElement>}
29
+ */
30
+ const usefulNodes = [];
31
+ collectUsefulNodes(node, usefulNodes);
32
+ if (usefulNodes.length === 0) {
33
+ detachNodeFromParent(node, parentNode);
34
+ }
35
+ // TODO remove in SVGO 3
36
+ for (const usefulNode of usefulNodes) {
37
+ // @ts-ignore parentNode is legacy
38
+ usefulNode.parentNode = node;
39
+ }
40
+ node.children = usefulNodes;
41
+ } else if (
42
+ elemsGroups.nonRendering.includes(node.name) &&
43
+ node.attributes.id == null
44
+ ) {
45
+ detachNodeFromParent(node, parentNode);
46
+ }
47
+ },
48
+ },
49
+ };
50
+ };
51
+
52
+ /**
53
+ * @type {(node: XastElement, usefulNodes: Array<XastElement>) => void}
54
+ */
55
+ const collectUsefulNodes = (node, usefulNodes) => {
56
+ for (const child of node.children) {
57
+ if (child.type === 'element') {
58
+ if (child.attributes.id != null || child.name === 'style') {
59
+ usefulNodes.push(child);
60
+ } else {
61
+ collectUsefulNodes(child, usefulNodes);
62
+ }
63
+ }
64
+ }
65
+ };
@@ -0,0 +1,144 @@
1
+ 'use strict';
2
+
3
+ const { visit, visitSkip, detachNodeFromParent } = require('../lib/xast.js');
4
+ const { collectStylesheet, computeStyle } = require('../lib/style.js');
5
+ const { elemsGroups } = require('./_collections.js');
6
+
7
+ exports.type = 'visitor';
8
+ exports.name = 'removeUselessStrokeAndFill';
9
+ exports.active = true;
10
+ exports.description = 'removes useless stroke and fill attributes';
11
+
12
+ /**
13
+ * Remove useless stroke and fill attrs.
14
+ *
15
+ * @author Kir Belevich
16
+ *
17
+ * @type {import('../lib/types').Plugin<{
18
+ * stroke?: boolean,
19
+ * fill?: boolean,
20
+ * removeNone?: boolean
21
+ * }>}
22
+ */
23
+ exports.fn = (root, params) => {
24
+ const {
25
+ stroke: removeStroke = true,
26
+ fill: removeFill = true,
27
+ removeNone = false,
28
+ } = params;
29
+
30
+ // style and script elements deoptimise this plugin
31
+ let hasStyleOrScript = false;
32
+ visit(root, {
33
+ element: {
34
+ enter: (node) => {
35
+ if (node.name === 'style' || node.name === 'script') {
36
+ hasStyleOrScript = true;
37
+ }
38
+ },
39
+ },
40
+ });
41
+ if (hasStyleOrScript) {
42
+ return null;
43
+ }
44
+
45
+ const stylesheet = collectStylesheet(root);
46
+
47
+ return {
48
+ element: {
49
+ enter: (node, parentNode) => {
50
+ // id attribute deoptimise the whole subtree
51
+ if (node.attributes.id != null) {
52
+ return visitSkip;
53
+ }
54
+ if (elemsGroups.shape.includes(node.name) == false) {
55
+ return;
56
+ }
57
+ const computedStyle = computeStyle(stylesheet, node);
58
+ const stroke = computedStyle.stroke;
59
+ const strokeOpacity = computedStyle['stroke-opacity'];
60
+ const strokeWidth = computedStyle['stroke-width'];
61
+ const markerEnd = computedStyle['marker-end'];
62
+ const fill = computedStyle.fill;
63
+ const fillOpacity = computedStyle['fill-opacity'];
64
+ const computedParentStyle =
65
+ parentNode.type === 'element'
66
+ ? computeStyle(stylesheet, parentNode)
67
+ : null;
68
+ const parentStroke =
69
+ computedParentStyle == null ? null : computedParentStyle.stroke;
70
+
71
+ // remove stroke*
72
+ if (removeStroke) {
73
+ if (
74
+ stroke == null ||
75
+ (stroke.type === 'static' && stroke.value == 'none') ||
76
+ (strokeOpacity != null &&
77
+ strokeOpacity.type === 'static' &&
78
+ strokeOpacity.value === '0') ||
79
+ (strokeWidth != null &&
80
+ strokeWidth.type === 'static' &&
81
+ strokeWidth.value === '0')
82
+ ) {
83
+ // stroke-width may affect the size of marker-end
84
+ // marker is not visible when stroke-width is 0
85
+ if (
86
+ (strokeWidth != null &&
87
+ strokeWidth.type === 'static' &&
88
+ strokeWidth.value === '0') ||
89
+ markerEnd == null
90
+ ) {
91
+ for (const name of Object.keys(node.attributes)) {
92
+ if (name.startsWith('stroke')) {
93
+ delete node.attributes[name];
94
+ }
95
+ }
96
+ // set explicit none to not inherit from parent
97
+ if (
98
+ parentStroke != null &&
99
+ parentStroke.type === 'static' &&
100
+ parentStroke.value !== 'none'
101
+ ) {
102
+ node.attributes.stroke = 'none';
103
+ }
104
+ }
105
+ }
106
+ }
107
+
108
+ // remove fill*
109
+ if (removeFill) {
110
+ if (
111
+ (fill != null && fill.type === 'static' && fill.value === 'none') ||
112
+ (fillOpacity != null &&
113
+ fillOpacity.type === 'static' &&
114
+ fillOpacity.value === '0')
115
+ ) {
116
+ for (const name of Object.keys(node.attributes)) {
117
+ if (name.startsWith('fill-')) {
118
+ delete node.attributes[name];
119
+ }
120
+ }
121
+ if (
122
+ fill == null ||
123
+ (fill.type === 'static' && fill.value !== 'none')
124
+ ) {
125
+ node.attributes.fill = 'none';
126
+ }
127
+ }
128
+ }
129
+
130
+ if (removeNone) {
131
+ if (
132
+ (stroke == null || node.attributes.stroke === 'none') &&
133
+ ((fill != null &&
134
+ fill.type === 'static' &&
135
+ fill.value === 'none') ||
136
+ node.attributes.fill === 'none')
137
+ ) {
138
+ detachNodeFromParent(node, parentNode);
139
+ }
140
+ }
141
+ },
142
+ },
143
+ };
144
+ };
@@ -0,0 +1,51 @@
1
+ 'use strict';
2
+
3
+ exports.type = 'visitor';
4
+ exports.name = 'removeViewBox';
5
+ exports.active = true;
6
+ exports.description = 'removes viewBox attribute when possible';
7
+
8
+ const viewBoxElems = ['svg', 'pattern', 'symbol'];
9
+
10
+ /**
11
+ * Remove viewBox attr which coincides with a width/height box.
12
+ *
13
+ * @see https://www.w3.org/TR/SVG11/coords.html#ViewBoxAttribute
14
+ *
15
+ * @example
16
+ * <svg width="100" height="50" viewBox="0 0 100 50">
17
+ * ⬇
18
+ * <svg width="100" height="50">
19
+ *
20
+ * @author Kir Belevich
21
+ *
22
+ * @type {import('../lib/types').Plugin<void>}
23
+ */
24
+ exports.fn = () => {
25
+ return {
26
+ element: {
27
+ enter: (node, parentNode) => {
28
+ if (
29
+ viewBoxElems.includes(node.name) &&
30
+ node.attributes.viewBox != null &&
31
+ node.attributes.width != null &&
32
+ node.attributes.height != null
33
+ ) {
34
+ // TODO remove width/height for such case instead
35
+ if (node.name === 'svg' && parentNode.type !== 'root') {
36
+ return;
37
+ }
38
+ const nums = node.attributes.viewBox.split(/[ ,]+/g);
39
+ if (
40
+ nums[0] === '0' &&
41
+ nums[1] === '0' &&
42
+ node.attributes.width.replace(/px$/, '') === nums[2] && // could use parseFloat too
43
+ node.attributes.height.replace(/px$/, '') === nums[3]
44
+ ) {
45
+ delete node.attributes.viewBox;
46
+ }
47
+ }
48
+ },
49
+ },
50
+ };
51
+ };
@@ -0,0 +1,30 @@
1
+ 'use strict';
2
+
3
+ exports.name = 'removeXMLNS';
4
+
5
+ exports.type = 'perItem';
6
+
7
+ exports.active = false;
8
+
9
+ exports.description =
10
+ 'removes xmlns attribute (for inline svg, disabled by default)';
11
+
12
+ /**
13
+ * Remove the xmlns attribute when present.
14
+ *
15
+ * @example
16
+ * <svg viewBox="0 0 100 50" xmlns="http://www.w3.org/2000/svg">
17
+ * ↓
18
+ * <svg viewBox="0 0 100 50">
19
+ *
20
+ * @param {Object} item current iteration item
21
+ * @return {Boolean} if true, xmlns will be filtered out
22
+ *
23
+ * @author Ricardo Tomasi
24
+ */
25
+ exports.fn = function (item) {
26
+ if (item.type === 'element' && item.name === 'svg') {
27
+ delete item.attributes.xmlns;
28
+ delete item.attributes['xmlns:xlink'];
29
+ }
30
+ };
@@ -0,0 +1,30 @@
1
+ 'use strict';
2
+
3
+ const { detachNodeFromParent } = require('../lib/xast.js');
4
+
5
+ exports.name = 'removeXMLProcInst';
6
+ exports.type = 'visitor';
7
+ exports.active = true;
8
+ exports.description = 'removes XML processing instructions';
9
+
10
+ /**
11
+ * Remove XML Processing Instruction.
12
+ *
13
+ * @example
14
+ * <?xml version="1.0" encoding="utf-8"?>
15
+ *
16
+ * @author Kir Belevich
17
+ *
18
+ * @type {import('../lib/types').Plugin<void>}
19
+ */
20
+ exports.fn = () => {
21
+ return {
22
+ instruction: {
23
+ enter: (node, parentNode) => {
24
+ if (node.name === 'xml') {
25
+ detachNodeFromParent(node, parentNode);
26
+ }
27
+ },
28
+ },
29
+ };
30
+ };