@iconify/tools 3.0.5 → 3.0.6

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.
@@ -512,7 +512,7 @@ class IconSet {
512
512
  return count;
513
513
  }
514
514
  /**
515
- * Remove icon
515
+ * Rename icon
516
516
  */
517
517
  rename(oldName, newName) {
518
518
  const entries = this.entries;
@@ -117,7 +117,7 @@ declare class IconSet {
117
117
  */
118
118
  remove(name: string, removeDependencies?: boolean | string): number;
119
119
  /**
120
- * Remove icon
120
+ * Rename icon
121
121
  */
122
122
  rename(oldName: string, newName: string): boolean;
123
123
  /**
@@ -510,7 +510,7 @@ class IconSet {
510
510
  return count;
511
511
  }
512
512
  /**
513
- * Remove icon
513
+ * Rename icon
514
514
  */
515
515
  rename(oldName, newName) {
516
516
  const entries = this.entries;
package/lib/index.cjs CHANGED
@@ -32,8 +32,10 @@ const colors_parse = require('./colors/parse.cjs');
32
32
  const colors_validate = require('./colors/validate.cjs');
33
33
  const colors_detect = require('./colors/detect.cjs');
34
34
  const optimise_svgo = require('./optimise/svgo.cjs');
35
+ const optimise_figma = require('./optimise/figma.cjs');
35
36
  const optimise_flags = require('./optimise/flags.cjs');
36
37
  const optimise_origin = require('./optimise/origin.cjs');
38
+ const optimise_mask = require('./optimise/mask.cjs');
37
39
  const optimise_scale = require('./optimise/scale.cjs');
38
40
  const optimise_globalStyle = require('./optimise/global-style.cjs');
39
41
  const export_directory = require('./export/directory.cjs');
@@ -130,8 +132,10 @@ exports.validateColors = colors_validate.validateColors;
130
132
  exports.validateColorsSync = colors_validate.validateColorsSync;
131
133
  exports.detectIconSetPalette = colors_detect.detectIconSetPalette;
132
134
  exports.runSVGO = optimise_svgo.runSVGO;
135
+ exports.removeFigmaClipPathFromSVG = optimise_figma.removeFigmaClipPathFromSVG;
133
136
  exports.deOptimisePaths = optimise_flags.deOptimisePaths;
134
137
  exports.resetSVGOrigin = optimise_origin.resetSVGOrigin;
138
+ exports.convertSVGToMask = optimise_mask.convertSVGToMask;
135
139
  exports.scaleSVG = optimise_scale.scaleSVG;
136
140
  exports.cleanupGlobalStyle = optimise_globalStyle.cleanupGlobalStyle;
137
141
  exports.exportToDirectory = export_directory.exportToDirectory;
package/lib/index.d.ts CHANGED
@@ -30,8 +30,10 @@ export { isEmptyColor, parseColors, parseColorsSync } from './colors/parse.js';
30
30
  export { validateColors, validateColorsSync } from './colors/validate.js';
31
31
  export { detectIconSetPalette } from './colors/detect.js';
32
32
  export { runSVGO } from './optimise/svgo.js';
33
+ export { removeFigmaClipPathFromSVG } from './optimise/figma.js';
33
34
  export { deOptimisePaths } from './optimise/flags.js';
34
35
  export { resetSVGOrigin } from './optimise/origin.js';
36
+ export { convertSVGToMask } from './optimise/mask.js';
35
37
  export { scaleSVG } from './optimise/scale.js';
36
38
  export { cleanupGlobalStyle } from './optimise/global-style.js';
37
39
  export { exportToDirectory } from './export/directory.js';
package/lib/index.mjs CHANGED
@@ -30,8 +30,10 @@ export { isEmptyColor, parseColors, parseColorsSync } from './colors/parse.mjs';
30
30
  export { validateColors, validateColorsSync } from './colors/validate.mjs';
31
31
  export { detectIconSetPalette } from './colors/detect.mjs';
32
32
  export { runSVGO } from './optimise/svgo.mjs';
33
+ export { removeFigmaClipPathFromSVG } from './optimise/figma.mjs';
33
34
  export { deOptimisePaths } from './optimise/flags.mjs';
34
35
  export { resetSVGOrigin } from './optimise/origin.mjs';
36
+ export { convertSVGToMask } from './optimise/mask.mjs';
35
37
  export { scaleSVG } from './optimise/scale.mjs';
36
38
  export { cleanupGlobalStyle } from './optimise/global-style.mjs';
37
39
  export { exportToDirectory } from './export/directory.mjs';
@@ -0,0 +1,175 @@
1
+ 'use strict';
2
+
3
+ const svg_data_tags = require('../svg/data/tags.cjs');
4
+
5
+ function isTinyNumber(value, limit) {
6
+ const num = parseInt(value);
7
+ return !isNaN(num) && Math.abs(num) < limit;
8
+ }
9
+ function checkClipPathNode(clipNode, expectedWidth, expectedHeight) {
10
+ for (const attr in clipNode.attribs) {
11
+ if (attr !== "id") {
12
+ return false;
13
+ }
14
+ }
15
+ const children = clipNode.children.filter((node) => node.type !== "text");
16
+ if (children.length !== 1) {
17
+ return false;
18
+ }
19
+ const childNode = children[0];
20
+ if (childNode.type !== "tag" || childNode.children.length) {
21
+ return false;
22
+ }
23
+ const attribs = {
24
+ ...childNode.attribs
25
+ };
26
+ delete attribs["fill"];
27
+ const fill = (childNode.attribs["fill"] ?? "").toLowerCase();
28
+ if (fill !== "white" && fill !== "#fff" && fill !== "#ffffff") {
29
+ console.warn(
30
+ "Unxepected fill on clip path:",
31
+ childNode.attribs["fill"]
32
+ );
33
+ return false;
34
+ }
35
+ switch (childNode.tagName) {
36
+ case "rect": {
37
+ const width = parseInt(childNode.attribs["width"]);
38
+ const height = parseInt(childNode.attribs["height"]);
39
+ if (width !== expectedWidth || height !== expectedHeight) {
40
+ console.warn("Invalid size of clip path");
41
+ return false;
42
+ }
43
+ delete attribs["width"];
44
+ delete attribs["height"];
45
+ break;
46
+ }
47
+ default:
48
+ console.warn(
49
+ "Unexpected tag in Figma clip path:",
50
+ childNode.tagName
51
+ );
52
+ return false;
53
+ }
54
+ Object.keys(attribs).forEach((attr) => {
55
+ const value = attribs[attr];
56
+ switch (attr) {
57
+ case "transform": {
58
+ const translateStart = "translate(";
59
+ const translateEnd = ")";
60
+ if (value.startsWith(translateStart) && value.endsWith(translateEnd)) {
61
+ const translateParts = value.slice(translateStart.length, 0 - translateEnd.length).split(/\s+/);
62
+ const limit = Math.min(expectedWidth, expectedHeight) / 1e3;
63
+ if (translateParts.length === 2 && isTinyNumber(translateParts[0], limit) && isTinyNumber(translateParts[1], limit)) {
64
+ delete attribs[attr];
65
+ }
66
+ }
67
+ }
68
+ }
69
+ });
70
+ return {
71
+ node: clipNode,
72
+ attribs
73
+ };
74
+ }
75
+ const urlStart = "url(#";
76
+ const urlEnd = ")";
77
+ function removeFigmaClipPathFromSVG(svg) {
78
+ const cheerio = svg.$svg;
79
+ const $root = svg.$svg(":root");
80
+ const children = $root.children();
81
+ const backup = svg.toString();
82
+ const shapesToClip = [];
83
+ let clipID;
84
+ for (let i = 0; i < children.length; i++) {
85
+ const node = children[i];
86
+ if (node.type === "tag") {
87
+ const tagName = node.tagName;
88
+ if (!svg_data_tags.defsTag.has(tagName) && !svg_data_tags.maskTags.has(tagName) && !svg_data_tags.symbolTag.has(tagName)) {
89
+ const clipPath2 = node.attribs["clip-path"];
90
+ if (!clipPath2 || !clipPath2.startsWith(urlStart) || !clipPath2.endsWith(urlEnd)) {
91
+ return false;
92
+ }
93
+ const id = clipPath2.slice(urlStart.length, 0 - urlEnd.length);
94
+ if (typeof clipID === "string" && clipID !== id) {
95
+ return false;
96
+ }
97
+ clipID = id;
98
+ shapesToClip.push(node);
99
+ }
100
+ }
101
+ }
102
+ if (typeof clipID !== "string") {
103
+ return false;
104
+ }
105
+ const checkClipPath = (node) => {
106
+ const id = node.attribs["id"];
107
+ if (id !== clipID) {
108
+ return;
109
+ }
110
+ const result = checkClipPathNode(
111
+ node,
112
+ svg.viewBox.width,
113
+ svg.viewBox.height
114
+ );
115
+ cheerio(node).remove();
116
+ return result;
117
+ };
118
+ const findClipPath = () => {
119
+ for (let i = 0; i < children.length; i++) {
120
+ const node = children[i];
121
+ if (node.type === "tag") {
122
+ const tagName = node.tagName;
123
+ if (svg_data_tags.defsTag.has(tagName)) {
124
+ const defsChildren = node.children;
125
+ for (let j = 0; j < defsChildren.length; j++) {
126
+ const childNode = defsChildren[j];
127
+ if (childNode.type === "tag" && childNode.tagName === "clipPath") {
128
+ const result = checkClipPath(childNode);
129
+ if (result !== void 0) {
130
+ const validChildren = node.children.filter(
131
+ (test) => {
132
+ if (test.type === "text") {
133
+ return false;
134
+ }
135
+ return true;
136
+ }
137
+ );
138
+ if (!validChildren.length) {
139
+ cheerio(node).remove();
140
+ }
141
+ return result;
142
+ }
143
+ }
144
+ }
145
+ }
146
+ if (tagName === "clipPath") {
147
+ const result = checkClipPath(node);
148
+ if (result !== void 0) {
149
+ return result;
150
+ }
151
+ }
152
+ }
153
+ }
154
+ };
155
+ const clipPath = findClipPath();
156
+ if (!clipPath) {
157
+ svg.load(backup);
158
+ return false;
159
+ }
160
+ const attribs = clipPath.attribs;
161
+ for (let i = 0; i < shapesToClip.length; i++) {
162
+ const node = shapesToClip[i];
163
+ cheerio(node).removeAttr("clip-path");
164
+ for (const attr in attribs) {
165
+ if (node.attribs[attr] !== void 0) {
166
+ svg.load(backup);
167
+ return false;
168
+ }
169
+ cheerio(node).attr(attr, attribs[attr]);
170
+ }
171
+ }
172
+ return true;
173
+ }
174
+
175
+ exports.removeFigmaClipPathFromSVG = removeFigmaClipPathFromSVG;
@@ -0,0 +1,10 @@
1
+ import { SVG } from '../svg/index.js';
2
+ import '@iconify/types';
3
+ import '@iconify/utils/lib/customisations/defaults';
4
+
5
+ /**
6
+ * Removes clip path from SVG, which Figma adds to icons that might have overflowing elements
7
+ */
8
+ declare function removeFigmaClipPathFromSVG(svg: SVG): boolean;
9
+
10
+ export { removeFigmaClipPathFromSVG };
@@ -0,0 +1,173 @@
1
+ import { defsTag, maskTags, symbolTag } from '../svg/data/tags.mjs';
2
+
3
+ function isTinyNumber(value, limit) {
4
+ const num = parseInt(value);
5
+ return !isNaN(num) && Math.abs(num) < limit;
6
+ }
7
+ function checkClipPathNode(clipNode, expectedWidth, expectedHeight) {
8
+ for (const attr in clipNode.attribs) {
9
+ if (attr !== "id") {
10
+ return false;
11
+ }
12
+ }
13
+ const children = clipNode.children.filter((node) => node.type !== "text");
14
+ if (children.length !== 1) {
15
+ return false;
16
+ }
17
+ const childNode = children[0];
18
+ if (childNode.type !== "tag" || childNode.children.length) {
19
+ return false;
20
+ }
21
+ const attribs = {
22
+ ...childNode.attribs
23
+ };
24
+ delete attribs["fill"];
25
+ const fill = (childNode.attribs["fill"] ?? "").toLowerCase();
26
+ if (fill !== "white" && fill !== "#fff" && fill !== "#ffffff") {
27
+ console.warn(
28
+ "Unxepected fill on clip path:",
29
+ childNode.attribs["fill"]
30
+ );
31
+ return false;
32
+ }
33
+ switch (childNode.tagName) {
34
+ case "rect": {
35
+ const width = parseInt(childNode.attribs["width"]);
36
+ const height = parseInt(childNode.attribs["height"]);
37
+ if (width !== expectedWidth || height !== expectedHeight) {
38
+ console.warn("Invalid size of clip path");
39
+ return false;
40
+ }
41
+ delete attribs["width"];
42
+ delete attribs["height"];
43
+ break;
44
+ }
45
+ default:
46
+ console.warn(
47
+ "Unexpected tag in Figma clip path:",
48
+ childNode.tagName
49
+ );
50
+ return false;
51
+ }
52
+ Object.keys(attribs).forEach((attr) => {
53
+ const value = attribs[attr];
54
+ switch (attr) {
55
+ case "transform": {
56
+ const translateStart = "translate(";
57
+ const translateEnd = ")";
58
+ if (value.startsWith(translateStart) && value.endsWith(translateEnd)) {
59
+ const translateParts = value.slice(translateStart.length, 0 - translateEnd.length).split(/\s+/);
60
+ const limit = Math.min(expectedWidth, expectedHeight) / 1e3;
61
+ if (translateParts.length === 2 && isTinyNumber(translateParts[0], limit) && isTinyNumber(translateParts[1], limit)) {
62
+ delete attribs[attr];
63
+ }
64
+ }
65
+ }
66
+ }
67
+ });
68
+ return {
69
+ node: clipNode,
70
+ attribs
71
+ };
72
+ }
73
+ const urlStart = "url(#";
74
+ const urlEnd = ")";
75
+ function removeFigmaClipPathFromSVG(svg) {
76
+ const cheerio = svg.$svg;
77
+ const $root = svg.$svg(":root");
78
+ const children = $root.children();
79
+ const backup = svg.toString();
80
+ const shapesToClip = [];
81
+ let clipID;
82
+ for (let i = 0; i < children.length; i++) {
83
+ const node = children[i];
84
+ if (node.type === "tag") {
85
+ const tagName = node.tagName;
86
+ if (!defsTag.has(tagName) && !maskTags.has(tagName) && !symbolTag.has(tagName)) {
87
+ const clipPath2 = node.attribs["clip-path"];
88
+ if (!clipPath2 || !clipPath2.startsWith(urlStart) || !clipPath2.endsWith(urlEnd)) {
89
+ return false;
90
+ }
91
+ const id = clipPath2.slice(urlStart.length, 0 - urlEnd.length);
92
+ if (typeof clipID === "string" && clipID !== id) {
93
+ return false;
94
+ }
95
+ clipID = id;
96
+ shapesToClip.push(node);
97
+ }
98
+ }
99
+ }
100
+ if (typeof clipID !== "string") {
101
+ return false;
102
+ }
103
+ const checkClipPath = (node) => {
104
+ const id = node.attribs["id"];
105
+ if (id !== clipID) {
106
+ return;
107
+ }
108
+ const result = checkClipPathNode(
109
+ node,
110
+ svg.viewBox.width,
111
+ svg.viewBox.height
112
+ );
113
+ cheerio(node).remove();
114
+ return result;
115
+ };
116
+ const findClipPath = () => {
117
+ for (let i = 0; i < children.length; i++) {
118
+ const node = children[i];
119
+ if (node.type === "tag") {
120
+ const tagName = node.tagName;
121
+ if (defsTag.has(tagName)) {
122
+ const defsChildren = node.children;
123
+ for (let j = 0; j < defsChildren.length; j++) {
124
+ const childNode = defsChildren[j];
125
+ if (childNode.type === "tag" && childNode.tagName === "clipPath") {
126
+ const result = checkClipPath(childNode);
127
+ if (result !== void 0) {
128
+ const validChildren = node.children.filter(
129
+ (test) => {
130
+ if (test.type === "text") {
131
+ return false;
132
+ }
133
+ return true;
134
+ }
135
+ );
136
+ if (!validChildren.length) {
137
+ cheerio(node).remove();
138
+ }
139
+ return result;
140
+ }
141
+ }
142
+ }
143
+ }
144
+ if (tagName === "clipPath") {
145
+ const result = checkClipPath(node);
146
+ if (result !== void 0) {
147
+ return result;
148
+ }
149
+ }
150
+ }
151
+ }
152
+ };
153
+ const clipPath = findClipPath();
154
+ if (!clipPath) {
155
+ svg.load(backup);
156
+ return false;
157
+ }
158
+ const attribs = clipPath.attribs;
159
+ for (let i = 0; i < shapesToClip.length; i++) {
160
+ const node = shapesToClip[i];
161
+ cheerio(node).removeAttr("clip-path");
162
+ for (const attr in attribs) {
163
+ if (node.attribs[attr] !== void 0) {
164
+ svg.load(backup);
165
+ return false;
166
+ }
167
+ cheerio(node).attr(attr, attribs[attr]);
168
+ }
169
+ }
170
+ return true;
171
+ }
172
+
173
+ export { removeFigmaClipPathFromSVG };
@@ -0,0 +1,110 @@
1
+ 'use strict';
2
+
3
+ const colors_parse = require('../colors/parse.cjs');
4
+ const utils = require('@iconify/utils');
5
+ require('@iconify/utils/lib/colors');
6
+ require('../svg/data/tags.cjs');
7
+ require('../svg/parse-style.cjs');
8
+ require('../css/parse.cjs');
9
+ require('../css/parser/tokens.cjs');
10
+ require('../css/parser/error.cjs');
11
+ require('../css/parser/strings.cjs');
12
+ require('../css/parser/text.cjs');
13
+ require('../css/parser/export.cjs');
14
+ require('../css/parser/tree.cjs');
15
+ require('../svg/parse.cjs');
16
+ require('../colors/attribs.cjs');
17
+ require('../svg/data/attributes.cjs');
18
+ require('../svg/analyse.cjs');
19
+ require('../svg/analyse/error.cjs');
20
+
21
+ const defaultBlackColors = ["black", "#000", "#000000"];
22
+ const defaultWhiteColors = ["white", "#fff", "#ffffff"];
23
+ const defaultOptions = {
24
+ color: "currentColor",
25
+ solid: [...defaultBlackColors, "currentcolor"],
26
+ transparent: defaultWhiteColors,
27
+ force: false,
28
+ id: "mask"
29
+ };
30
+ function convertSVGToMask(svg, options = {}) {
31
+ const props = {
32
+ ...defaultOptions,
33
+ ...options
34
+ };
35
+ const check = (test, value, color) => {
36
+ if (typeof test === "string") {
37
+ return value.toLowerCase() === test;
38
+ }
39
+ if (test instanceof Array) {
40
+ return test.includes(value.toLowerCase());
41
+ }
42
+ return test(value, color);
43
+ };
44
+ let foundSolid = false;
45
+ let foundTransparent = false;
46
+ let failed = false;
47
+ let hasCustomValue = false;
48
+ const backup = svg.toString();
49
+ colors_parse.parseColorsSync(svg, {
50
+ callback: (attr, colorStr, color) => {
51
+ if (!color || colors_parse.isEmptyColor(color)) {
52
+ return colorStr;
53
+ }
54
+ if (check(props.solid, colorStr, color)) {
55
+ foundSolid = true;
56
+ return "#fff";
57
+ }
58
+ if (check(props.transparent, colorStr, color)) {
59
+ foundTransparent = true;
60
+ return "#000";
61
+ }
62
+ if (props.custom) {
63
+ let customValue = props.custom(colorStr, color);
64
+ if (typeof customValue === "number") {
65
+ const num = Math.max(
66
+ Math.min(Math.round(customValue * 255), 255),
67
+ 0
68
+ );
69
+ let str = num.toString(16);
70
+ if (str.length < 2) {
71
+ str = "0" + str;
72
+ }
73
+ if (str[0] === str[1]) {
74
+ str = str[0];
75
+ }
76
+ customValue = "#" + str + str + str;
77
+ }
78
+ if (typeof customValue === "string") {
79
+ if (defaultBlackColors.includes(customValue)) {
80
+ foundSolid = true;
81
+ } else if (defaultWhiteColors.includes(customValue)) {
82
+ foundTransparent = true;
83
+ } else {
84
+ hasCustomValue = true;
85
+ }
86
+ return customValue;
87
+ }
88
+ }
89
+ failed = true;
90
+ console.warn("Unexpected color:", colorStr);
91
+ return color;
92
+ }
93
+ });
94
+ const hasColors = hasCustomValue || foundSolid && foundTransparent;
95
+ if (failed || !hasColors && !props.force) {
96
+ svg.load(backup);
97
+ return false;
98
+ }
99
+ const parsed = utils.parseSVGContent(svg.toString());
100
+ if (!parsed) {
101
+ return false;
102
+ }
103
+ const { defs, content } = utils.splitSVGDefs(parsed.body);
104
+ const newBody = `<defs>${defs}<mask id="${props.id}">${content}</mask></defs><rect mask="url(#${props.id})" ${svg.viewBox.left ? `x=${svg.viewBox.left} ` : ""}${svg.viewBox.top ? `y=${svg.viewBox.top} ` : ""}width="${svg.viewBox.width}" height="${svg.viewBox.height}" fill="${props.color}" />`;
105
+ const newContent = utils.iconToHTML(newBody, parsed.attribs);
106
+ svg.load(newContent);
107
+ return true;
108
+ }
109
+
110
+ exports.convertSVGToMask = convertSVGToMask;
@@ -0,0 +1,23 @@
1
+ import { Color } from '@iconify/utils/lib/colors/types';
2
+ import { SVG } from '../svg/index.js';
3
+ import '@iconify/types';
4
+ import '@iconify/utils/lib/customisations/defaults';
5
+
6
+ type ColorCallback = (value: string, color: Color | null) => boolean;
7
+ type ColorCheck = string | string[] | ColorCallback;
8
+ interface SVGToMaskOptions {
9
+ color?: string;
10
+ solid?: ColorCheck;
11
+ transparent?: ColorCheck;
12
+ custom?: (value: string, color: Color | null) => string | number | undefined;
13
+ force?: boolean;
14
+ id?: string;
15
+ }
16
+ /**
17
+ * Converts SVG to mask
18
+ *
19
+ * Fixes badly designed icons, which use white shape where icon supposed to be transparent
20
+ */
21
+ declare function convertSVGToMask(svg: SVG, options?: SVGToMaskOptions): boolean;
22
+
23
+ export { convertSVGToMask };
@@ -0,0 +1,108 @@
1
+ import { parseColorsSync, isEmptyColor } from '../colors/parse.mjs';
2
+ import { parseSVGContent, splitSVGDefs, iconToHTML } from '@iconify/utils';
3
+ import '@iconify/utils/lib/colors';
4
+ import '../svg/data/tags.mjs';
5
+ import '../svg/parse-style.mjs';
6
+ import '../css/parse.mjs';
7
+ import '../css/parser/tokens.mjs';
8
+ import '../css/parser/error.mjs';
9
+ import '../css/parser/strings.mjs';
10
+ import '../css/parser/text.mjs';
11
+ import '../css/parser/export.mjs';
12
+ import '../css/parser/tree.mjs';
13
+ import '../svg/parse.mjs';
14
+ import '../colors/attribs.mjs';
15
+ import '../svg/data/attributes.mjs';
16
+ import '../svg/analyse.mjs';
17
+ import '../svg/analyse/error.mjs';
18
+
19
+ const defaultBlackColors = ["black", "#000", "#000000"];
20
+ const defaultWhiteColors = ["white", "#fff", "#ffffff"];
21
+ const defaultOptions = {
22
+ color: "currentColor",
23
+ solid: [...defaultBlackColors, "currentcolor"],
24
+ transparent: defaultWhiteColors,
25
+ force: false,
26
+ id: "mask"
27
+ };
28
+ function convertSVGToMask(svg, options = {}) {
29
+ const props = {
30
+ ...defaultOptions,
31
+ ...options
32
+ };
33
+ const check = (test, value, color) => {
34
+ if (typeof test === "string") {
35
+ return value.toLowerCase() === test;
36
+ }
37
+ if (test instanceof Array) {
38
+ return test.includes(value.toLowerCase());
39
+ }
40
+ return test(value, color);
41
+ };
42
+ let foundSolid = false;
43
+ let foundTransparent = false;
44
+ let failed = false;
45
+ let hasCustomValue = false;
46
+ const backup = svg.toString();
47
+ parseColorsSync(svg, {
48
+ callback: (attr, colorStr, color) => {
49
+ if (!color || isEmptyColor(color)) {
50
+ return colorStr;
51
+ }
52
+ if (check(props.solid, colorStr, color)) {
53
+ foundSolid = true;
54
+ return "#fff";
55
+ }
56
+ if (check(props.transparent, colorStr, color)) {
57
+ foundTransparent = true;
58
+ return "#000";
59
+ }
60
+ if (props.custom) {
61
+ let customValue = props.custom(colorStr, color);
62
+ if (typeof customValue === "number") {
63
+ const num = Math.max(
64
+ Math.min(Math.round(customValue * 255), 255),
65
+ 0
66
+ );
67
+ let str = num.toString(16);
68
+ if (str.length < 2) {
69
+ str = "0" + str;
70
+ }
71
+ if (str[0] === str[1]) {
72
+ str = str[0];
73
+ }
74
+ customValue = "#" + str + str + str;
75
+ }
76
+ if (typeof customValue === "string") {
77
+ if (defaultBlackColors.includes(customValue)) {
78
+ foundSolid = true;
79
+ } else if (defaultWhiteColors.includes(customValue)) {
80
+ foundTransparent = true;
81
+ } else {
82
+ hasCustomValue = true;
83
+ }
84
+ return customValue;
85
+ }
86
+ }
87
+ failed = true;
88
+ console.warn("Unexpected color:", colorStr);
89
+ return color;
90
+ }
91
+ });
92
+ const hasColors = hasCustomValue || foundSolid && foundTransparent;
93
+ if (failed || !hasColors && !props.force) {
94
+ svg.load(backup);
95
+ return false;
96
+ }
97
+ const parsed = parseSVGContent(svg.toString());
98
+ if (!parsed) {
99
+ return false;
100
+ }
101
+ const { defs, content } = splitSVGDefs(parsed.body);
102
+ const newBody = `<defs>${defs}<mask id="${props.id}">${content}</mask></defs><rect mask="url(#${props.id})" ${svg.viewBox.left ? `x=${svg.viewBox.left} ` : ""}${svg.viewBox.top ? `y=${svg.viewBox.top} ` : ""}width="${svg.viewBox.width}" height="${svg.viewBox.height}" fill="${props.color}" />`;
103
+ const newContent = iconToHTML(newBody, parsed.attribs);
104
+ svg.load(newContent);
105
+ return true;
106
+ }
107
+
108
+ export { convertSVGToMask };
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "type": "module",
4
4
  "description": "Collection of functions for cleaning up and parsing SVG for Iconify project",
5
5
  "author": "Vjacheslav Trushkin",
6
- "version": "3.0.5",
6
+ "version": "3.0.6",
7
7
  "license": "MIT",
8
8
  "bugs": "https://github.com/iconify/tools/issues",
9
9
  "homepage": "https://github.com/iconify/tools",
@@ -16,32 +16,32 @@
16
16
  "types": "./lib/index.d.ts",
17
17
  "dependencies": {
18
18
  "@iconify/types": "^2.0.0",
19
- "@iconify/utils": "^2.1.7",
20
- "@types/cheerio": "^0.22.31",
21
- "@types/tar": "^6.1.5",
19
+ "@iconify/utils": "^2.1.10",
20
+ "@types/cheerio": "^0.22.32",
21
+ "@types/tar": "^6.1.6",
22
22
  "cheerio": "^1.0.0-rc.12",
23
23
  "extract-zip": "^2.0.1",
24
24
  "local-pkg": "^0.4.3",
25
25
  "pathe": "^1.1.1",
26
26
  "svgo": "^3.0.2",
27
- "tar": "^6.1.15"
27
+ "tar": "^6.2.0"
28
28
  },
29
29
  "devDependencies": {
30
- "@types/jest": "^29.5.3",
31
- "@types/node": "^18.17.3",
30
+ "@types/jest": "^29.5.5",
31
+ "@types/node": "^18.18.0",
32
32
  "@typescript-eslint/eslint-plugin": "^5.62.0",
33
33
  "@typescript-eslint/parser": "^5.62.0",
34
34
  "cross-env": "^7.0.3",
35
- "eslint": "^8.46.0",
35
+ "eslint": "^8.50.0",
36
36
  "eslint-config-prettier": "^8.10.0",
37
37
  "eslint-plugin-jasmine": "^4.1.3",
38
38
  "eslint-plugin-prettier": "^4.2.1",
39
39
  "jasmine": "^5.1.0",
40
- "jest": "^29.6.2",
40
+ "jest": "^29.7.0",
41
41
  "prettier": "^2.8.8",
42
- "rimraf": "^5.0.1",
42
+ "rimraf": "^5.0.5",
43
43
  "ts-jest": "^29.1.1",
44
- "typescript": "^5.1.6",
44
+ "typescript": "^5.2.2",
45
45
  "unbuild": "^1.2.1"
46
46
  },
47
47
  "exports": {
@@ -411,6 +411,11 @@
411
411
  "require": "./lib/misc/write-json.cjs",
412
412
  "import": "./lib/misc/write-json.mjs"
413
413
  },
414
+ "./lib/optimise/figma": {
415
+ "types": "./lib/optimise/figma.d.ts",
416
+ "require": "./lib/optimise/figma.cjs",
417
+ "import": "./lib/optimise/figma.mjs"
418
+ },
414
419
  "./lib/optimise/flags": {
415
420
  "types": "./lib/optimise/flags.d.ts",
416
421
  "require": "./lib/optimise/flags.cjs",
@@ -421,6 +426,11 @@
421
426
  "require": "./lib/optimise/global-style.cjs",
422
427
  "import": "./lib/optimise/global-style.mjs"
423
428
  },
429
+ "./lib/optimise/mask": {
430
+ "types": "./lib/optimise/mask.d.ts",
431
+ "require": "./lib/optimise/mask.cjs",
432
+ "import": "./lib/optimise/mask.mjs"
433
+ },
424
434
  "./lib/optimise/origin": {
425
435
  "types": "./lib/optimise/origin.d.ts",
426
436
  "require": "./lib/optimise/origin.cjs",