export-svg-typescript 0.1.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.
- package/demo/index.ts +104 -0
- package/demo/loading-bouncy-ball.svg +3 -0
- package/demo/loading-double-ring.svg +6 -0
- package/demo/loading-eclipse.svg +3 -0
- package/export-svg-typescript.js +177 -0
- package/package.json +15 -0
- package/readme.md +28 -0
package/demo/index.ts
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
// Do a Barrel Roll index of SVG icons as JS exports, enabling tree shaking to only the icons you import.
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Returns a customized SVG string for loading icon bouncy-ball
|
|
6
|
+
*
|
|
7
|
+
* 
|
|
8
|
+
* @param {Object} options - Configuration options
|
|
9
|
+
* @param {string[]} [options.colors] - Array of hex colors to replace existing colors
|
|
10
|
+
* @param {number} [options.width] - Width of the SVG (default: 200)
|
|
11
|
+
* @param {number} [options.height] - Height of the SVG (default: 200)
|
|
12
|
+
* @param {number} [options.size] - Size for both width and height
|
|
13
|
+
* @example loadingBouncyBall({ colors: ['#0099e5', '#ff4c4c'], size: 100 });
|
|
14
|
+
* @returns {string} SVG string with applied customizations
|
|
15
|
+
*/
|
|
16
|
+
export const loadingBouncyBall = (options: LoadingOptions = {}) => customSVG(options,
|
|
17
|
+
`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid" width="200" height="200" style="shape-rendering: auto; display: block; "><g><circle fill="#e15b64" r="13" cy="23" cx="50"><animate values="23;77;23" keyTimes="0;0.5;1" keySplines="0.45 0 0.9 0.55;0 0.45 0.55 0.9" calcMode="spline" repeatCount="indefinite" dur="1s" attributeName="cy"></animate></circle><g></g></g></svg>`);
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Returns a customized SVG string for loading icon double-ring
|
|
22
|
+
*
|
|
23
|
+
* 
|
|
24
|
+
* @param {Object} options - Configuration options
|
|
25
|
+
* @param {string[]} [options.colors] - Array of hex colors to replace existing colors
|
|
26
|
+
* @param {number} [options.width] - Width of the SVG (default: 200)
|
|
27
|
+
* @param {number} [options.height] - Height of the SVG (default: 200)
|
|
28
|
+
* @param {number} [options.size] - Size for both width and height
|
|
29
|
+
* @example loadingDoubleRing({ colors: ['#0099e5', '#ff4c4c'], size: 100 });
|
|
30
|
+
* @returns {string} SVG string with applied customizations
|
|
31
|
+
*/
|
|
32
|
+
export const loadingDoubleRing = (options: LoadingOptions = {}) => customSVG(options,
|
|
33
|
+
`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid" width="200" height="200" style="shape-rendering: auto; display: block; "><g><circle stroke-linecap="round" fill="none" stroke-dasharray="50.26548245743669 50.26548245743669" stroke="#0099e5" stroke-width="8" r="32" cy="50" cx="50"><animateTransform values="0 50 50;360 50 50" keyTimes="0;1" repeatCount="indefinite" dur="1s" type="rotate" attributeName="transform"></animateTransform></circle><circle stroke-linecap="round" fill="none" stroke-dashoffset="36.12831551628262" stroke-dasharray="36.12831551628262 36.12831551628262" stroke="#ff4c4c" stroke-width="8" r="23" cy="50" cx="50"><animateTransform values="0 50 50;-360 50 50" keyTimes="0;1" repeatCount="indefinite" dur="1s" type="rotate" attributeName="transform"></animateTransform></circle><g></g></g></svg>`);
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Returns a customized SVG string for loading icon eclipse
|
|
38
|
+
*
|
|
39
|
+
* 
|
|
40
|
+
* @param {Object} options - Configuration options
|
|
41
|
+
* @param {string[]} [options.colors] - Array of hex colors to replace existing colors
|
|
42
|
+
* @param {number} [options.width] - Width of the SVG (default: 200)
|
|
43
|
+
* @param {number} [options.height] - Height of the SVG (default: 200)
|
|
44
|
+
* @param {number} [options.size] - Size for both width and height
|
|
45
|
+
* @example loadingEclipse({ colors: ['#0099e5', '#ff4c4c'], size: 100 });
|
|
46
|
+
* @returns {string} SVG string with applied customizations
|
|
47
|
+
*/
|
|
48
|
+
export const loadingEclipse = (options: LoadingOptions = {}) => customSVG(options,
|
|
49
|
+
`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid" width="200" height="200" style="shape-rendering: auto; display: block;"><g><path stroke="none" fill="#ffb900" d="M10 50A40 40 0 0 0 90 50A40 42 0 0 1 10 50"><animateTransform values="0 50 51;360 50 51" keyTimes="0;1" repeatCount="indefinite" dur="1s" type="rotate" attributeName="transform"></animateTransform></path><g></g></g></svg>`);
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Shared utility function for processing SVG icons
|
|
53
|
+
* @param {Object} options - Configuration options
|
|
54
|
+
* @param {boolean} [options.raw] - Whether to return the raw SVG string or an img tag
|
|
55
|
+
* @param {string[]} [options.colors] - Array of hex colors to replace existing colors
|
|
56
|
+
* @param {number|string} [options.width] - Width of the SVG
|
|
57
|
+
* @param {number|string} [options.height] - Height of the SVG
|
|
58
|
+
* @param {number|string} [options.size] - Size for both width and height (overrides width/height)
|
|
59
|
+
* @param {string} svgString - The original SVG content
|
|
60
|
+
* @returns {string} SVG string with applied customizations
|
|
61
|
+
*/
|
|
62
|
+
function customSVG( options: LoadingOptions, svgString: string) {
|
|
63
|
+
const { colors = [], width, height, size, raw = false } = options;
|
|
64
|
+
|
|
65
|
+
const widthMatch = svgString.match(/width="(d+)"/);
|
|
66
|
+
const heightMatch = svgString.match(/height="(d+)"/);
|
|
67
|
+
const finalWidth = size || width || widthMatch?.[1] || '100';
|
|
68
|
+
const finalHeight = size || height || heightMatch?.[1] || '100';
|
|
69
|
+
|
|
70
|
+
svgString = svgString.replace(/width="[^"]*"/g, `width="${finalWidth}"`);
|
|
71
|
+
svgString = svgString.replace(/height="[^"]*"/g, `height="${finalHeight}"`);
|
|
72
|
+
|
|
73
|
+
// If colors array is provided, replace hex colors in order of appearance
|
|
74
|
+
if (colors && colors.length > 0) {
|
|
75
|
+
const hexColorRegex = /#[0-9a-fA-F]{6}|#[0-9a-fA-F]{3}/g;
|
|
76
|
+
let colorIndex = 0;
|
|
77
|
+
|
|
78
|
+
svgString = svgString.replace(hexColorRegex, (match) => {
|
|
79
|
+
if (colorIndex < colors.length) {
|
|
80
|
+
const replacement = colors[colorIndex];
|
|
81
|
+
colorIndex++;
|
|
82
|
+
return replacement?.startsWith('#') ? replacement : `#${replacement}`;
|
|
83
|
+
}
|
|
84
|
+
return match; // Keep original color if no replacement available
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
if (!raw)
|
|
88
|
+
svgString = `<img alt="loading-icon" src="data:image/svg+xml;utf8,${encodeURIComponent(svgString)}" />`
|
|
89
|
+
|
|
90
|
+
return svgString;
|
|
91
|
+
}
|
|
92
|
+
interface LoadingOptions {
|
|
93
|
+
/** Array of hex colors to replace existing colors, in order of appearance in SVG*/
|
|
94
|
+
colors?: string[];
|
|
95
|
+
/** Width of the SVG */
|
|
96
|
+
width?: number;
|
|
97
|
+
/** Height of the SVG */
|
|
98
|
+
height?: number;
|
|
99
|
+
/** Size for both width and height (overrides width/height) */
|
|
100
|
+
size?: number;
|
|
101
|
+
/** Whether to return the raw SVG string or an img tag */
|
|
102
|
+
raw?: boolean;
|
|
103
|
+
}
|
|
104
|
+
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid" width="200" height="200" style="shape-rendering: auto; display: block; " xmlns:xlink="http://www.w3.org/1999/xlink"><g><circle fill="#e15b64" r="13" cy="23" cx="50">
|
|
2
|
+
<animate values="23;77;23" keyTimes="0;0.5;1" keySplines="0.45 0 0.9 0.55;0 0.45 0.55 0.9" calcMode="spline" repeatCount="indefinite" dur="1s" attributeName="cy"></animate>
|
|
3
|
+
</circle><g></g></g></svg>
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid" width="200" height="200" style="shape-rendering: auto; display: block; " xmlns:xlink="http://www.w3.org/1999/xlink"><g><circle stroke-linecap="round" fill="none" stroke-dasharray="50.26548245743669 50.26548245743669" stroke="#0099e5" stroke-width="8" r="32" cy="50" cx="50">
|
|
2
|
+
<animateTransform values="0 50 50;360 50 50" keyTimes="0;1" repeatCount="indefinite" dur="1s" type="rotate" attributeName="transform"></animateTransform>
|
|
3
|
+
</circle>
|
|
4
|
+
<circle stroke-linecap="round" fill="none" stroke-dashoffset="36.12831551628262" stroke-dasharray="36.12831551628262 36.12831551628262" stroke="#ff4c4c" stroke-width="8" r="23" cy="50" cx="50">
|
|
5
|
+
<animateTransform values="0 50 50;-360 50 50" keyTimes="0;1" repeatCount="indefinite" dur="1s" type="rotate" attributeName="transform"></animateTransform>
|
|
6
|
+
</circle><g></g></g></svg>
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid" width="200" height="200" style="shape-rendering: auto; display: block;"><g><path stroke="none" fill="#ffb900" d="M10 50A40 40 0 0 0 90 50A40 42 0 0 1 10 50">
|
|
2
|
+
<animateTransform values="0 50 51;360 50 51" keyTimes="0;1" repeatCount="indefinite" dur="1s" type="rotate" attributeName="transform"></animateTransform>
|
|
3
|
+
</path><g></g></g></svg>
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import fs from "fs";
|
|
4
|
+
import path from "path";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Script to process SVG files from input folder and generates JS export index,
|
|
8
|
+
* Barrel Roll for the icons js files, enabling tree shaking to only the icons the
|
|
9
|
+
* user imports, enables customizing colors and size, and shows tooltip preview.
|
|
10
|
+
* usage: node convert-svg-to-js.js -i ./src/icons-svg -o ./dist/icons
|
|
11
|
+
* @param {string} inputFolder - Path to folder containing SVG files
|
|
12
|
+
* @param {string} outputFolder - Path to folder where JS files will be generated
|
|
13
|
+
*/
|
|
14
|
+
export function convertSVGFolderToExportIndex(inputFolder, indexPath) {
|
|
15
|
+
// Read all files from input folder
|
|
16
|
+
const files = fs.readdirSync(inputFolder);
|
|
17
|
+
const svgFiles = files.filter(
|
|
18
|
+
(file) => path.extname(file).toLowerCase() === ".svg"
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
// Generate index file
|
|
22
|
+
|
|
23
|
+
let indexContent = `// Do a Barrel Roll index of SVG icons as JS exports, enabling tree shaking to only the icons you import.\n\n`;
|
|
24
|
+
const exportData = [];
|
|
25
|
+
|
|
26
|
+
svgFiles.forEach((svgFile) => {
|
|
27
|
+
const inputPath = path.join(inputFolder, svgFile);
|
|
28
|
+
const baseName = path.basename(svgFile, ".svg");
|
|
29
|
+
|
|
30
|
+
// Read SVG content
|
|
31
|
+
const svgContent = fs.readFileSync(inputPath, "utf8");
|
|
32
|
+
|
|
33
|
+
// Extract original dimensions from SVG
|
|
34
|
+
const widthMatch = svgContent.match(/width="(\d+)"/);
|
|
35
|
+
const heightMatch = svgContent.match(/height="(\d+)"/);
|
|
36
|
+
const defaultWidth = widthMatch ? widthMatch[1] : "100";
|
|
37
|
+
const defaultHeight = heightMatch ? heightMatch[1] : "100";
|
|
38
|
+
|
|
39
|
+
// Convert SVG string to Base64
|
|
40
|
+
const svgBase64 = `data:image/svg+xml;base64,${btoa(
|
|
41
|
+
unescape(encodeURIComponent(svgContent))
|
|
42
|
+
)}`;
|
|
43
|
+
|
|
44
|
+
const jsContent = `
|
|
45
|
+
/**
|
|
46
|
+
* Returns a customized SVG string for loading icon ${baseName.replace(/loading-/g, "" )}
|
|
47
|
+
*
|
|
48
|
+
* 
|
|
49
|
+
* @param {Object} options - Configuration options
|
|
50
|
+
* @param {string[]} [options.colors] - Array of hex colors to replace existing colors
|
|
51
|
+
* @param {number} [options.width] - Width of the SVG (default: ${defaultWidth})
|
|
52
|
+
* @param {number} [options.height] - Height of the SVG (default: ${defaultHeight})
|
|
53
|
+
* @param {number} [options.size] - Size for both width and height
|
|
54
|
+
* @example ${toCamelCase(
|
|
55
|
+
baseName
|
|
56
|
+
)}({ colors: ['#0099e5', '#ff4c4c'], size: 100 });
|
|
57
|
+
* @returns {string} SVG string with applied customizations
|
|
58
|
+
*/
|
|
59
|
+
export const ${toCamelCase(baseName)} = (options: LoadingOptions = {}) => customSVG(options,
|
|
60
|
+
\`${svgContent
|
|
61
|
+
.replace(/`/g, "\\`")
|
|
62
|
+
.replace(' xmlns:xlink="http://www.w3.org/1999/xlink"', "")
|
|
63
|
+
.replace(/\n/g, "")
|
|
64
|
+
.replace(/\$/g, "\\$")
|
|
65
|
+
.replace(/ +/g, " ")
|
|
66
|
+
.replace(/> </g, "><")}\`);\n\n`;
|
|
67
|
+
|
|
68
|
+
// Generate imports
|
|
69
|
+
indexContent += jsContent;
|
|
70
|
+
|
|
71
|
+
// Store export data for index file
|
|
72
|
+
exportData.push({
|
|
73
|
+
fileName: svgFile,
|
|
74
|
+
baseName: baseName,
|
|
75
|
+
functionName: toCamelCase(baseName),
|
|
76
|
+
content: jsContent,
|
|
77
|
+
svgBase64: svgBase64,
|
|
78
|
+
defaultWidth: defaultWidth,
|
|
79
|
+
defaultHeight: defaultHeight,
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
indexContent += `/**
|
|
84
|
+
* Shared utility function for processing SVG icons
|
|
85
|
+
* @param {Object} options - Configuration options
|
|
86
|
+
* @param {boolean} [options.raw] - Whether to return the raw SVG string or an img tag
|
|
87
|
+
* @param {string[]} [options.colors] - Array of hex colors to replace existing colors
|
|
88
|
+
* @param {number|string} [options.width] - Width of the SVG
|
|
89
|
+
* @param {number|string} [options.height] - Height of the SVG
|
|
90
|
+
* @param {number|string} [options.size] - Size for both width and height (overrides width/height)
|
|
91
|
+
* @param {string} svgString - The original SVG content
|
|
92
|
+
* @returns {string} SVG string with applied customizations
|
|
93
|
+
*/
|
|
94
|
+
function customSVG( options: LoadingOptions, svgString: string) {
|
|
95
|
+
const { colors = [], width, height, size, raw = false } = options;
|
|
96
|
+
|
|
97
|
+
const widthMatch = svgString.match(/width="(d+)"/);
|
|
98
|
+
const heightMatch = svgString.match(/height="(d+)"/);
|
|
99
|
+
const finalWidth = size || width || widthMatch?.[1] || '100';
|
|
100
|
+
const finalHeight = size || height || heightMatch?.[1] || '100';
|
|
101
|
+
|
|
102
|
+
svgString = svgString.replace(/width="[^"]*"/g, \`width="\${finalWidth}"\`);
|
|
103
|
+
svgString = svgString.replace(/height="[^"]*"/g, \`height="\${finalHeight}"\`);
|
|
104
|
+
|
|
105
|
+
// If colors array is provided, replace hex colors in order of appearance
|
|
106
|
+
if (colors && colors.length > 0) {
|
|
107
|
+
const hexColorRegex = /#[0-9a-fA-F]{6}|#[0-9a-fA-F]{3}/g;
|
|
108
|
+
let colorIndex = 0;
|
|
109
|
+
|
|
110
|
+
svgString = svgString.replace(hexColorRegex, (match) => {
|
|
111
|
+
if (colorIndex < colors.length) {
|
|
112
|
+
const replacement = colors[colorIndex];
|
|
113
|
+
colorIndex++;
|
|
114
|
+
return replacement?.startsWith('#') ? replacement : \`#\${replacement}\`;
|
|
115
|
+
}
|
|
116
|
+
return match; // Keep original color if no replacement available
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
if (!raw)
|
|
120
|
+
svgString = \`<img alt="loading-icon" src="data:image/svg+xml;utf8,\${encodeURIComponent(svgString)}" />\`
|
|
121
|
+
|
|
122
|
+
return svgString;
|
|
123
|
+
}
|
|
124
|
+
interface LoadingOptions {
|
|
125
|
+
/** Array of hex colors to replace existing colors, in order of appearance in SVG*/
|
|
126
|
+
colors?: string[];
|
|
127
|
+
/** Width of the SVG */
|
|
128
|
+
width?: number;
|
|
129
|
+
/** Height of the SVG */
|
|
130
|
+
height?: number;
|
|
131
|
+
/** Size for both width and height (overrides width/height) */
|
|
132
|
+
size?: number;
|
|
133
|
+
/** Whether to return the raw SVG string or an img tag */
|
|
134
|
+
raw?: boolean;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
`;
|
|
138
|
+
|
|
139
|
+
fs.writeFileSync(indexPath, indexContent);
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
console.log(
|
|
145
|
+
`✨ Converted ${svgFiles.length} SVG files to customizable JS export files`
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Convert string to camelCase
|
|
151
|
+
* @param {string} str
|
|
152
|
+
* @returns {string}
|
|
153
|
+
*/
|
|
154
|
+
function toCamelCase(str) {
|
|
155
|
+
return str
|
|
156
|
+
.replace(/[^a-zA-Z0-9]/g, " ")
|
|
157
|
+
.replace(/\s+/g, " ")
|
|
158
|
+
.split(" ")
|
|
159
|
+
.map((word, index) => {
|
|
160
|
+
if (index === 0) {
|
|
161
|
+
return word.toLowerCase();
|
|
162
|
+
}
|
|
163
|
+
return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
|
|
164
|
+
})
|
|
165
|
+
.join("");
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Get input/output folders from command line args
|
|
169
|
+
const args = process.argv.slice(2);
|
|
170
|
+
const inputIndex = args.indexOf("-i");
|
|
171
|
+
const outputIndex = args.indexOf("-o");
|
|
172
|
+
|
|
173
|
+
const inputFolder = inputIndex >= 0 ? args[inputIndex + 1] : "./svg"; // Folder containing SVG files
|
|
174
|
+
const outputPath = outputIndex >= 0 ? args[outputIndex + 1] : "./index.ts"; // Folder for generated JS files
|
|
175
|
+
|
|
176
|
+
// Run the converter
|
|
177
|
+
convertSVGFolderToExportIndex(inputFolder, outputPath);
|
package/package.json
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "export-svg-typescript",
|
|
3
|
+
"module": "convert-svg-to-export.js",
|
|
4
|
+
"author": "vtempest",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"version": "0.1.0",
|
|
7
|
+
"description": "Export SVG to TypeScript",
|
|
8
|
+
"type": "module",
|
|
9
|
+
"scripts": {
|
|
10
|
+
"demo": "node export-svg-typescript.js -i ./demo -o ./demo/index.ts"
|
|
11
|
+
},
|
|
12
|
+
"bin": {
|
|
13
|
+
"export-svg-typescript": "convert-svg-to-export.js"
|
|
14
|
+
}
|
|
15
|
+
}
|
package/readme.md
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
## export-svg-typescript
|
|
2
|
+
|
|
3
|
+
Convert a folder of SVG icons into a color-customizable, tree-shakable TypeScript export `index.ts` that works with any component framework without SVG or Vite compiler issues.
|
|
4
|
+
|
|
5
|
+
1. Barrel Roll: Exports all icons as named functions for tree shaking to only the ones actually used.
|
|
6
|
+
2. Typescript Tooltip Previews: Each export includes a tooltip preview (Base64-encoded).
|
|
7
|
+
3. Customizable: Change icon colors, size, and dimensions at runtime. Can return SVG or IMG tag with SVG as source.
|
|
8
|
+
4. CLI Tool: Use directly from the command line or in npm scripts: `npx
|
|
9
|
+
|
|
10
|
+
### Install
|
|
11
|
+
```
|
|
12
|
+
npm install -g export-svg-typescript
|
|
13
|
+
```
|
|
14
|
+
```
|
|
15
|
+
# or use npx without installing globally
|
|
16
|
+
npx export-svg-typescript -i ./src/icons -o ./src/icons/index.ts
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
### Example
|
|
20
|
+
|
|
21
|
+
Clone this repo and run `npm run demo` to see icons in demo folder.
|
|
22
|
+
|
|
23
|
+
```javascript
|
|
24
|
+
import { loadingDoubleRing } from './demo';
|
|
25
|
+
|
|
26
|
+
loadingDoubleRing({size: 200, colors: ["#5345bb"] })
|
|
27
|
+
```
|
|
28
|
+

|