svgfusion 1.0.0 → 1.0.2
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/README.md +19 -23
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +356 -167
- package/dist/index.d.mts +15 -9
- package/dist/index.d.ts +15 -9
- package/dist/index.js +20 -98
- package/dist/index.mjs +20 -85
- package/package.json +21 -13
package/README.md
CHANGED
|
@@ -1,33 +1,33 @@
|
|
|
1
1
|
<div align="center">
|
|
2
|
-
<img src="
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
2
|
+
<img src="https://i.ibb.co/TZFfpFL/logo.png" alt="SVGFusion Logo" width="120" height="120">
|
|
3
|
+
|
|
4
|
+
# SVGFusion
|
|
5
|
+
|
|
6
|
+
**Transform SVG files into production-ready React and Vue 3 components**
|
|
7
|
+
|
|
8
|
+
A powerful Node.js CLI tool and library that converts SVG files into optimized React and Vue components with complex SVG support, TypeScript integration, and smart optimization for modern development workflows.
|
|
9
|
+
|
|
10
|
+
[](https://badge.fury.io/js/svgfusion)
|
|
11
|
+
[](https://www.typescriptlang.org/)
|
|
12
|
+
[](https://reactjs.org/)
|
|
13
|
+
[](https://vuejs.org/)
|
|
14
|
+
|
|
15
|
+
[📚 Documentation](https://svgfusion.netlify.app) • [⚡ CLI Reference](https://svgfusion.netlify.app/docs/cli-usage) • [📦 NPM](https://www.npmjs.com/package/svgfusion)
|
|
16
|
+
|
|
17
17
|
</div>
|
|
18
18
|
|
|
19
19
|
## Features
|
|
20
20
|
|
|
21
21
|
- **Dual Framework Support**: Generate both React and Vue 3 components from the same SVG
|
|
22
|
+
- **Complex SVG Support**: Handles gradients, masks, filters, patterns, and Figma exports
|
|
23
|
+
- **ID Collision Prevention**: Automatic unique ID generation for complex SVGs
|
|
22
24
|
- **Optimized Output**: Built-in SVGO optimization with customizable settings
|
|
23
|
-
- **
|
|
24
|
-
- **Class-Based Styling**: Supports CSS classes, Tailwind, and framework-specific styling
|
|
25
|
+
- **Icon Builder Ready**: Perfect for design systems and icon libraries
|
|
25
26
|
- **TypeScript Ready**: Full TypeScript support with proper type definitions
|
|
26
27
|
- **Flexible API**: Both CLI and programmatic usage
|
|
27
28
|
- **Batch Processing**: Convert entire directories of SVG files
|
|
28
|
-
- **
|
|
29
|
+
- **Production Ready**: Robust output with proper error handling
|
|
29
30
|
- **Zero Configuration**: Works out of the box with sensible defaults
|
|
30
|
-
- **Production Ready**: Optimized output with proper TypeScript types
|
|
31
31
|
|
|
32
32
|
## Quick Start
|
|
33
33
|
|
|
@@ -343,7 +343,3 @@ Contributions are welcome! Please read [CONTRIBUTING.md](./CONTRIBUTING.md) for
|
|
|
343
343
|
## Changelog
|
|
344
344
|
|
|
345
345
|
See [CHANGELOG.md](./CHANGELOG.md) for details.
|
|
346
|
-
|
|
347
|
-
---
|
|
348
|
-
|
|
349
|
-
**Made with care for the developer community**
|
package/dist/cli.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
|
|
2
|
-
export {
|
|
2
|
+
export { }
|
package/dist/cli.js
CHANGED
|
@@ -6,6 +6,13 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
|
6
6
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
7
|
var __getProtoOf = Object.getPrototypeOf;
|
|
8
8
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __esm = (fn, res) => function __init() {
|
|
10
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
11
|
+
};
|
|
12
|
+
var __export = (target, all) => {
|
|
13
|
+
for (var name in all)
|
|
14
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
15
|
+
};
|
|
9
16
|
var __copyProps = (to, from, except, desc) => {
|
|
10
17
|
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
18
|
for (let key of __getOwnPropNames(from))
|
|
@@ -22,18 +29,29 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
22
29
|
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
23
30
|
mod
|
|
24
31
|
));
|
|
32
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
25
33
|
|
|
26
|
-
// node_modules/tsup/assets/cjs_shims.js
|
|
27
|
-
var getImportMetaUrl
|
|
28
|
-
var
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
34
|
+
// node_modules/.pnpm/tsup@8.5.0_typescript@5.8.3/node_modules/tsup/assets/cjs_shims.js
|
|
35
|
+
var getImportMetaUrl, importMetaUrl;
|
|
36
|
+
var init_cjs_shims = __esm({
|
|
37
|
+
"node_modules/.pnpm/tsup@8.5.0_typescript@5.8.3/node_modules/tsup/assets/cjs_shims.js"() {
|
|
38
|
+
"use strict";
|
|
39
|
+
getImportMetaUrl = () => typeof document === "undefined" ? new URL(`file:${__filename}`).href : document.currentScript && document.currentScript.src || new URL("main.js", document.baseURI).href;
|
|
40
|
+
importMetaUrl = /* @__PURE__ */ getImportMetaUrl();
|
|
41
|
+
}
|
|
42
|
+
});
|
|
32
43
|
|
|
33
44
|
// src/utils/files.ts
|
|
34
|
-
var
|
|
35
|
-
|
|
36
|
-
|
|
45
|
+
var files_exports = {};
|
|
46
|
+
__export(files_exports, {
|
|
47
|
+
ensureDirectoryExists: () => ensureDirectoryExists,
|
|
48
|
+
getComponentFilename: () => getComponentFilename,
|
|
49
|
+
getFileExtension: () => getFileExtension,
|
|
50
|
+
readSvgDirectory: () => readSvgDirectory,
|
|
51
|
+
readSvgFile: () => readSvgFile,
|
|
52
|
+
writeComponentFile: () => writeComponentFile,
|
|
53
|
+
writeSvgFile: () => writeSvgFile
|
|
54
|
+
});
|
|
37
55
|
async function readSvgFile(filePath) {
|
|
38
56
|
try {
|
|
39
57
|
const content = await (0, import_promises.readFile)(filePath, "utf-8");
|
|
@@ -42,6 +60,14 @@ async function readSvgFile(filePath) {
|
|
|
42
60
|
throw new Error(`Failed to read SVG file: ${filePath}. ${error}`);
|
|
43
61
|
}
|
|
44
62
|
}
|
|
63
|
+
async function writeSvgFile(filePath, content) {
|
|
64
|
+
try {
|
|
65
|
+
await ensureDirectoryExists((0, import_path.dirname)(filePath));
|
|
66
|
+
await (0, import_promises.writeFile)(filePath, content, "utf-8");
|
|
67
|
+
} catch (error) {
|
|
68
|
+
throw new Error(`Failed to write SVG file: ${filePath}. ${error}`);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
45
71
|
async function writeComponentFile(filePath, content) {
|
|
46
72
|
try {
|
|
47
73
|
await ensureDirectoryExists((0, import_path.dirname)(filePath));
|
|
@@ -84,11 +110,31 @@ function getFileExtension(framework, typescript = true) {
|
|
|
84
110
|
function getComponentFilename(_svgFilename, componentName, extension) {
|
|
85
111
|
return `${componentName}${extension}`;
|
|
86
112
|
}
|
|
113
|
+
var import_promises, import_path, import_fs;
|
|
114
|
+
var init_files = __esm({
|
|
115
|
+
"src/utils/files.ts"() {
|
|
116
|
+
"use strict";
|
|
117
|
+
init_cjs_shims();
|
|
118
|
+
import_promises = require("fs/promises");
|
|
119
|
+
import_path = require("path");
|
|
120
|
+
import_fs = require("fs");
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
// src/cli.ts
|
|
125
|
+
init_cjs_shims();
|
|
126
|
+
var import_commander = require("commander");
|
|
127
|
+
init_files();
|
|
87
128
|
|
|
88
|
-
// src/
|
|
129
|
+
// src/core/react-converter.ts
|
|
130
|
+
init_cjs_shims();
|
|
89
131
|
var import_core = require("@svgr/core");
|
|
90
132
|
|
|
133
|
+
// src/core/converter.ts
|
|
134
|
+
init_cjs_shims();
|
|
135
|
+
|
|
91
136
|
// src/utils/svgo.ts
|
|
137
|
+
init_cjs_shims();
|
|
92
138
|
var import_svgo = require("svgo");
|
|
93
139
|
var defaultConfig = {
|
|
94
140
|
plugins: [
|
|
@@ -125,6 +171,7 @@ function optimizeSvg(svgContent, config = defaultConfig) {
|
|
|
125
171
|
}
|
|
126
172
|
|
|
127
173
|
// src/utils/name.ts
|
|
174
|
+
init_cjs_shims();
|
|
128
175
|
function pascalCase(str) {
|
|
129
176
|
return str.replace(/[^a-zA-Z0-9\s-_]/g, " ").split(/[\s-_]+/).filter((word) => word.length > 0).map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join("");
|
|
130
177
|
}
|
|
@@ -135,186 +182,328 @@ function formatComponentName(name, prefix, suffix) {
|
|
|
135
182
|
return `${prefixPart}${baseName}${suffixPart}`;
|
|
136
183
|
}
|
|
137
184
|
|
|
138
|
-
// src/
|
|
139
|
-
|
|
185
|
+
// src/core/converter.ts
|
|
186
|
+
var BaseConverter = class {
|
|
187
|
+
/**
|
|
188
|
+
* Process SVG content with optimization
|
|
189
|
+
*/
|
|
190
|
+
async processSvg(svgContent, options) {
|
|
191
|
+
const { optimize: optimize2 = true } = options;
|
|
192
|
+
if (optimize2) {
|
|
193
|
+
return optimizeSvg(svgContent);
|
|
194
|
+
}
|
|
195
|
+
return svgContent;
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Generate component name from options
|
|
199
|
+
*/
|
|
200
|
+
generateComponentName(options) {
|
|
201
|
+
const { name, prefix, suffix } = options;
|
|
202
|
+
const baseName = name || "Icon";
|
|
203
|
+
return formatComponentName(baseName, prefix, suffix);
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Generate filename for component
|
|
207
|
+
*/
|
|
208
|
+
generateFilename(componentName, framework, typescript = true) {
|
|
209
|
+
try {
|
|
210
|
+
const {
|
|
211
|
+
getFileExtension: getFileExtension2,
|
|
212
|
+
getComponentFilename: getComponentFilename2
|
|
213
|
+
} = (init_files(), __toCommonJS(files_exports));
|
|
214
|
+
const extension = getFileExtension2(framework, typescript);
|
|
215
|
+
return getComponentFilename2("icon.svg", componentName, extension);
|
|
216
|
+
} catch {
|
|
217
|
+
const extensions = {
|
|
218
|
+
react: typescript ? ".tsx" : ".jsx",
|
|
219
|
+
vue: ".vue"
|
|
220
|
+
};
|
|
221
|
+
return `${componentName}${extensions[framework]}`;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
// src/core/processors/svgr-processor.ts
|
|
227
|
+
init_cjs_shims();
|
|
228
|
+
function createSvgrConfig(options) {
|
|
140
229
|
const {
|
|
141
|
-
name,
|
|
142
|
-
prefix,
|
|
143
|
-
suffix,
|
|
144
|
-
optimize: optimize2 = true,
|
|
145
230
|
typescript = true,
|
|
146
231
|
memo = true,
|
|
147
232
|
ref = true,
|
|
148
233
|
titleProp = true,
|
|
149
|
-
descProp = true
|
|
234
|
+
descProp = true,
|
|
235
|
+
icon = true,
|
|
236
|
+
dimensions = false,
|
|
237
|
+
replaceAttrValues = { "#000": "currentColor", "#000000": "currentColor" },
|
|
238
|
+
svgProps = {},
|
|
239
|
+
expandProps = false,
|
|
240
|
+
nativeProps = true,
|
|
241
|
+
ariaLabelledBy = false,
|
|
242
|
+
ariaHidden = false,
|
|
243
|
+
role = "img"
|
|
150
244
|
} = options;
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
245
|
+
const config = {
|
|
246
|
+
typescript,
|
|
247
|
+
memo,
|
|
248
|
+
ref,
|
|
249
|
+
titleProp,
|
|
250
|
+
descProp,
|
|
251
|
+
icon,
|
|
252
|
+
dimensions,
|
|
253
|
+
expandProps,
|
|
254
|
+
svgProps: {
|
|
255
|
+
className: "{className}",
|
|
256
|
+
...ref && { ref: "{ref}" },
|
|
257
|
+
...nativeProps && {
|
|
258
|
+
width: "{width}",
|
|
259
|
+
height: "{height}",
|
|
260
|
+
style: "{style}"
|
|
261
|
+
},
|
|
262
|
+
// Accessibility attributes
|
|
263
|
+
...ariaLabelledBy && titleProp && descProp && {
|
|
264
|
+
"aria-labelledby": "{titleId} {descId}"
|
|
265
|
+
},
|
|
266
|
+
...ariaHidden && { "aria-hidden": "true" },
|
|
267
|
+
role,
|
|
268
|
+
...svgProps
|
|
269
|
+
},
|
|
270
|
+
replaceAttrValues,
|
|
271
|
+
plugins: ["@svgr/plugin-svgo", "@svgr/plugin-jsx", "@svgr/plugin-prettier"]
|
|
272
|
+
};
|
|
273
|
+
config.svgoConfig = {
|
|
274
|
+
plugins: [
|
|
275
|
+
{
|
|
276
|
+
name: "preset-default",
|
|
277
|
+
params: {
|
|
278
|
+
overrides: {
|
|
279
|
+
removeViewBox: false,
|
|
280
|
+
removeTitle: titleProp ? false : true,
|
|
281
|
+
removeDesc: descProp ? false : true,
|
|
282
|
+
removeUselessStrokeAndFill: false,
|
|
283
|
+
removeUnusedNS: false,
|
|
284
|
+
// Preserve complex features
|
|
285
|
+
removeUselessDefs: false,
|
|
286
|
+
convertShapeToPath: false,
|
|
287
|
+
mergePaths: false,
|
|
288
|
+
convertColors: false
|
|
289
|
+
// Disable color conversion to preserve gradients
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
},
|
|
293
|
+
...icon && !dimensions ? [{ name: "removeAttrs", params: { attrs: ["width", "height"] } }] : [],
|
|
294
|
+
...icon ? ["cleanupNumericValues"] : []
|
|
295
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
296
|
+
]
|
|
297
|
+
// Type assertion to handle SVGO v3 plugin configuration complexity
|
|
298
|
+
};
|
|
299
|
+
return config;
|
|
300
|
+
}
|
|
301
|
+
function postProcessReactComponent(svgrOutput, componentName, _options) {
|
|
302
|
+
let processedCode = svgrOutput;
|
|
303
|
+
if (hasComplexFeatures(processedCode)) {
|
|
304
|
+
processedCode = addUniqueIds(processedCode, componentName);
|
|
305
|
+
}
|
|
306
|
+
return processedCode;
|
|
307
|
+
}
|
|
308
|
+
function hasComplexFeatures(code) {
|
|
309
|
+
const complexFeatures = [
|
|
310
|
+
"linearGradient",
|
|
311
|
+
"radialGradient",
|
|
312
|
+
"pattern",
|
|
313
|
+
"mask",
|
|
314
|
+
"filter",
|
|
315
|
+
"clipPath",
|
|
316
|
+
"marker",
|
|
317
|
+
"symbol",
|
|
318
|
+
"use"
|
|
319
|
+
];
|
|
320
|
+
return complexFeatures.some(
|
|
321
|
+
(feature) => code.includes(`<${feature}`) || code.includes(`</${feature}`)
|
|
322
|
+
);
|
|
323
|
+
}
|
|
324
|
+
function addUniqueIds(code, componentName) {
|
|
325
|
+
const uniquePrefix = `${componentName.toLowerCase()}_`;
|
|
326
|
+
code = code.replace(/id="([^"]+)"/g, `id="${uniquePrefix}$1"`);
|
|
327
|
+
code = code.replace(/url\(#([^)]+)\)/g, `url(#${uniquePrefix}$1)`);
|
|
328
|
+
code = code.replace(/href="#([^"]+)"/g, `href="#${uniquePrefix}$1"`);
|
|
329
|
+
return code;
|
|
330
|
+
}
|
|
189
331
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
componentName
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
332
|
+
// src/core/react-converter.ts
|
|
333
|
+
var ReactConverter = class extends BaseConverter {
|
|
334
|
+
/**
|
|
335
|
+
* Convert SVG to React component using SVGR
|
|
336
|
+
*/
|
|
337
|
+
async convert(svgContent, options = {}) {
|
|
338
|
+
try {
|
|
339
|
+
const componentName = this.generateComponentName(options);
|
|
340
|
+
let processedSvg = await this.processSvg(svgContent, options);
|
|
341
|
+
if (hasComplexFeatures(processedSvg)) {
|
|
342
|
+
processedSvg = addUniqueIds(processedSvg, componentName);
|
|
343
|
+
}
|
|
344
|
+
const svgrConfig = createSvgrConfig(options);
|
|
345
|
+
const svgrResult = await (0, import_core.transform)(processedSvg, svgrConfig, {
|
|
346
|
+
componentName
|
|
347
|
+
});
|
|
348
|
+
const code = postProcessReactComponent(
|
|
349
|
+
svgrResult,
|
|
350
|
+
componentName,
|
|
351
|
+
options
|
|
352
|
+
);
|
|
353
|
+
const filename = this.generateFilename(
|
|
354
|
+
componentName,
|
|
355
|
+
"react",
|
|
356
|
+
options.typescript ?? true
|
|
357
|
+
);
|
|
358
|
+
return {
|
|
359
|
+
code,
|
|
360
|
+
filename,
|
|
361
|
+
componentName
|
|
362
|
+
};
|
|
363
|
+
} catch (error) {
|
|
364
|
+
throw new Error(`Failed to convert SVG to React: ${error}`);
|
|
365
|
+
}
|
|
201
366
|
}
|
|
367
|
+
};
|
|
368
|
+
async function convertToReact(svgContent, options = {}) {
|
|
369
|
+
const converter = new ReactConverter();
|
|
370
|
+
return converter.convert(svgContent, options);
|
|
202
371
|
}
|
|
203
372
|
|
|
204
|
-
// src/
|
|
205
|
-
|
|
373
|
+
// src/core/vue-converter.ts
|
|
374
|
+
init_cjs_shims();
|
|
375
|
+
|
|
376
|
+
// src/core/processors/vue-processor.ts
|
|
377
|
+
init_cjs_shims();
|
|
378
|
+
function generateVueComponent(svgContent, options) {
|
|
206
379
|
const {
|
|
207
380
|
name,
|
|
208
381
|
prefix,
|
|
209
382
|
suffix,
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
compositionApi: _compositionApi = true,
|
|
213
|
-
// eslint-disable-line @typescript-eslint/no-unused-vars
|
|
214
|
-
scriptSetup = true
|
|
383
|
+
props = true,
|
|
384
|
+
replaceAttrValues = { "#000": "currentColor", "#000000": "currentColor" }
|
|
215
385
|
} = options;
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
const componentName = formatComponentName(baseName, prefix, suffix);
|
|
223
|
-
const cleanedSvg = processedSvg.replace(/<\?xml[^>]*\?>\s*/, "").replace(/xmlns="[^"]*"/g, "").replace(/width="[^"]*"/g, "").replace(/height="[^"]*"/g, "").replace(/<svg/, '<svg v-bind="$attrs"').replace(/class="([^"]*)"/g, 'class="$1"').replace(/currentColor/g, "currentColor");
|
|
224
|
-
const scriptTag = scriptSetup ? generateScriptSetup(typescript, componentName) : generateCompositionScript(componentName, typescript);
|
|
225
|
-
const template = `<template>
|
|
226
|
-
${cleanedSvg}
|
|
227
|
-
</template>`;
|
|
228
|
-
const style = `<style scoped>
|
|
229
|
-
/* Add component-specific styles here */
|
|
230
|
-
</style>`;
|
|
231
|
-
const vueComponent = `${scriptTag}
|
|
232
|
-
|
|
233
|
-
${template}
|
|
234
|
-
|
|
235
|
-
${style}`;
|
|
236
|
-
const extension = getFileExtension("vue", typescript);
|
|
237
|
-
const filename = getComponentFilename("icon.svg", componentName, extension);
|
|
238
|
-
return {
|
|
239
|
-
code: vueComponent,
|
|
240
|
-
filename,
|
|
241
|
-
componentName
|
|
242
|
-
};
|
|
243
|
-
} catch (error) {
|
|
244
|
-
throw new Error(`Failed to convert SVG to Vue: ${error}`);
|
|
386
|
+
const baseName = name || "Icon";
|
|
387
|
+
const componentName = formatComponentName(baseName, prefix, suffix);
|
|
388
|
+
let processedSvg = cleanSvgForVue(svgContent);
|
|
389
|
+
processedSvg = applyAttributeReplacements(processedSvg, replaceAttrValues);
|
|
390
|
+
if (hasComplexFeatures2(processedSvg)) {
|
|
391
|
+
processedSvg = addUniqueIds2(processedSvg, componentName);
|
|
245
392
|
}
|
|
393
|
+
processedSvg = addVueAttributes(processedSvg, props);
|
|
394
|
+
const code = generateVueComponentCode(processedSvg, componentName, options);
|
|
395
|
+
return { code, componentName };
|
|
246
396
|
}
|
|
247
|
-
function
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
const
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
397
|
+
function cleanSvgForVue(svgContent) {
|
|
398
|
+
return svgContent.replace(/<\?xml[^>]*\?>\s*/, "").replace(/<!--[\s\S]*?-->/g, "").replace(/xmlns="[^"]*"/g, "").trim();
|
|
399
|
+
}
|
|
400
|
+
function applyAttributeReplacements(svg, replacements) {
|
|
401
|
+
let processedSvg = svg;
|
|
402
|
+
for (const [from, to] of Object.entries(replacements)) {
|
|
403
|
+
const regex = new RegExp(from.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "g");
|
|
404
|
+
processedSvg = processedSvg.replace(regex, to);
|
|
405
|
+
}
|
|
406
|
+
return processedSvg;
|
|
407
|
+
}
|
|
408
|
+
function hasComplexFeatures2(svg) {
|
|
409
|
+
const complexFeatures = [
|
|
410
|
+
"linearGradient",
|
|
411
|
+
"radialGradient",
|
|
412
|
+
"pattern",
|
|
413
|
+
"mask",
|
|
414
|
+
"filter",
|
|
415
|
+
"clipPath",
|
|
416
|
+
"marker",
|
|
417
|
+
"symbol",
|
|
418
|
+
"use"
|
|
419
|
+
];
|
|
420
|
+
return complexFeatures.some(
|
|
421
|
+
(feature) => svg.includes(`<${feature}`) || svg.includes(`</${feature}`)
|
|
422
|
+
);
|
|
268
423
|
}
|
|
269
|
-
function
|
|
270
|
-
const
|
|
271
|
-
|
|
424
|
+
function addUniqueIds2(svg, componentName) {
|
|
425
|
+
const uniquePrefix = `${componentName.toLowerCase()}_`;
|
|
426
|
+
svg = svg.replace(/id="([^"]+)"/g, `id="${uniquePrefix}$1"`);
|
|
427
|
+
svg = svg.replace(/url\(#([^)]+)\)/g, `url(#${uniquePrefix}$1)`);
|
|
428
|
+
svg = svg.replace(/href="#([^"]+)"/g, `href="#${uniquePrefix}$1"`);
|
|
429
|
+
return svg;
|
|
430
|
+
}
|
|
431
|
+
function addVueAttributes(svg, enableProps) {
|
|
432
|
+
if (!enableProps) return svg;
|
|
433
|
+
return svg.replace(
|
|
434
|
+
"<svg",
|
|
435
|
+
'<svg :class="className" :style="style" v-bind="$attrs"'
|
|
436
|
+
);
|
|
437
|
+
}
|
|
438
|
+
function generateVueComponentCode(svg, componentName, options) {
|
|
439
|
+
const { typescript, compositionApi, props } = options;
|
|
440
|
+
const scriptLang = typescript ? ' lang="ts"' : "";
|
|
441
|
+
const scriptSetup = compositionApi ? " setup" : "";
|
|
442
|
+
let script = `<script${scriptLang}${scriptSetup}>`;
|
|
443
|
+
if (compositionApi) {
|
|
444
|
+
if (props) {
|
|
445
|
+
script += `
|
|
272
446
|
interface Props {
|
|
273
|
-
|
|
274
|
-
style?:
|
|
275
|
-
}
|
|
276
|
-
const exportStatement = typescript ? `
|
|
277
|
-
const ${componentName} = defineComponent({
|
|
278
|
-
name: '${componentName}',
|
|
279
|
-
props: {
|
|
280
|
-
class: {
|
|
281
|
-
type: String,
|
|
282
|
-
default: '',
|
|
283
|
-
},
|
|
284
|
-
style: {
|
|
285
|
-
type: [String, Object],
|
|
286
|
-
default: undefined,
|
|
287
|
-
},
|
|
288
|
-
},
|
|
289
|
-
setup(props: Props) {
|
|
290
|
-
return {};
|
|
291
|
-
},
|
|
292
|
-
});
|
|
447
|
+
className?: string;
|
|
448
|
+
style?: Record<string, any>;
|
|
449
|
+
}
|
|
293
450
|
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
451
|
+
defineProps<Props>();
|
|
452
|
+
`;
|
|
453
|
+
}
|
|
454
|
+
} else {
|
|
455
|
+
script += `
|
|
456
|
+
export default {
|
|
297
457
|
name: '${componentName}',
|
|
298
458
|
props: {
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
default: '',
|
|
302
|
-
},
|
|
303
|
-
style: {
|
|
304
|
-
type: [String, Object],
|
|
305
|
-
default: undefined,
|
|
306
|
-
},
|
|
459
|
+
className: String,
|
|
460
|
+
style: Object,
|
|
307
461
|
},
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
}
|
|
311
|
-
|
|
462
|
+
};
|
|
463
|
+
`;
|
|
464
|
+
}
|
|
465
|
+
script += `</script>`;
|
|
466
|
+
const template = `
|
|
467
|
+
<template>
|
|
468
|
+
${svg}
|
|
469
|
+
</template>`;
|
|
470
|
+
const style = `
|
|
471
|
+
<style scoped>
|
|
472
|
+
/* Component styles */
|
|
473
|
+
</style>`;
|
|
474
|
+
return [script, template, style].filter(Boolean).join("\n");
|
|
475
|
+
}
|
|
312
476
|
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
477
|
+
// src/core/vue-converter.ts
|
|
478
|
+
var VueConverter = class extends BaseConverter {
|
|
479
|
+
/**
|
|
480
|
+
* Convert SVG to Vue component
|
|
481
|
+
*/
|
|
482
|
+
async convert(svgContent, options = {}) {
|
|
483
|
+
try {
|
|
484
|
+
const processedSvg = await this.processSvg(svgContent, options);
|
|
485
|
+
const { code, componentName } = generateVueComponent(
|
|
486
|
+
processedSvg,
|
|
487
|
+
options
|
|
488
|
+
);
|
|
489
|
+
const filename = this.generateFilename(
|
|
490
|
+
componentName,
|
|
491
|
+
"vue",
|
|
492
|
+
options.typescript ?? true
|
|
493
|
+
);
|
|
494
|
+
return {
|
|
495
|
+
code,
|
|
496
|
+
filename,
|
|
497
|
+
componentName
|
|
498
|
+
};
|
|
499
|
+
} catch (error) {
|
|
500
|
+
throw new Error(`Failed to convert SVG to Vue: ${error}`);
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
};
|
|
504
|
+
async function convertToVue(svgContent, options = {}) {
|
|
505
|
+
const converter = new VueConverter();
|
|
506
|
+
return converter.convert(svgContent, options);
|
|
318
507
|
}
|
|
319
508
|
|
|
320
509
|
// src/cli.ts
|
|
@@ -382,7 +571,7 @@ program.command("convert").description("Convert SVG files to React or Vue compon
|
|
|
382
571
|
for (const filePath of svgFiles) {
|
|
383
572
|
const svgContent = await readSvgFile(filePath);
|
|
384
573
|
const optimizedSvg = optimize2 ? optimizeSvg(svgContent) : svgContent;
|
|
385
|
-
const result = framework === "react" ? await convertToReact(optimizedSvg, { typescript }) : convertToVue(optimizedSvg, { typescript });
|
|
574
|
+
const result = framework === "react" ? await convertToReact(optimizedSvg, { typescript }) : await convertToVue(optimizedSvg, { typescript });
|
|
386
575
|
const outputPath = (0, import_path2.join)(output, result.filename);
|
|
387
576
|
await writeComponentFile(outputPath, result.code);
|
|
388
577
|
}
|
package/dist/index.d.mts
CHANGED
|
@@ -13,10 +13,22 @@ interface ReactConversionOptions extends ConversionOptions {
|
|
|
13
13
|
ref?: boolean;
|
|
14
14
|
titleProp?: boolean;
|
|
15
15
|
descProp?: boolean;
|
|
16
|
+
icon?: boolean;
|
|
17
|
+
dimensions?: boolean;
|
|
18
|
+
replaceAttrValues?: Record<string, string>;
|
|
19
|
+
svgProps?: Record<string, string>;
|
|
20
|
+
expandProps?: boolean | 'start' | 'end';
|
|
21
|
+
nativeProps?: boolean;
|
|
22
|
+
ariaLabelledBy?: boolean;
|
|
23
|
+
ariaHidden?: boolean;
|
|
24
|
+
role?: 'img' | 'graphics-document' | 'graphics-symbol' | 'presentation';
|
|
16
25
|
}
|
|
17
26
|
interface VueConversionOptions extends ConversionOptions {
|
|
18
27
|
compositionApi?: boolean;
|
|
19
28
|
scriptSetup?: boolean;
|
|
29
|
+
props?: boolean;
|
|
30
|
+
dimensions?: boolean;
|
|
31
|
+
replaceAttrValues?: Record<string, string>;
|
|
20
32
|
}
|
|
21
33
|
interface ConversionResult {
|
|
22
34
|
code: string;
|
|
@@ -60,19 +72,13 @@ interface CliOptions {
|
|
|
60
72
|
|
|
61
73
|
/**
|
|
62
74
|
* Convert SVG to React component
|
|
63
|
-
* @param svgContent - SVG content string
|
|
64
|
-
* @param options - Conversion options
|
|
65
|
-
* @returns Conversion result with React component code
|
|
66
75
|
*/
|
|
67
76
|
declare function convertToReact(svgContent: string, options?: ReactConversionOptions): Promise<ConversionResult>;
|
|
68
77
|
|
|
69
78
|
/**
|
|
70
|
-
* Convert SVG to Vue
|
|
71
|
-
* @param svgContent - SVG content string
|
|
72
|
-
* @param options - Conversion options
|
|
73
|
-
* @returns Conversion result with Vue component code
|
|
79
|
+
* Convert SVG to Vue component
|
|
74
80
|
*/
|
|
75
|
-
declare function convertToVue(svgContent: string, options?: VueConversionOptions): ConversionResult
|
|
81
|
+
declare function convertToVue(svgContent: string, options?: VueConversionOptions): Promise<ConversionResult>;
|
|
76
82
|
|
|
77
83
|
/**
|
|
78
84
|
* Optimize SVG content using SVGO
|
|
@@ -149,4 +155,4 @@ declare function pascalCase(str: string): string;
|
|
|
149
155
|
*/
|
|
150
156
|
declare function formatComponentName(name: string, prefix?: string, suffix?: string): string;
|
|
151
157
|
|
|
152
|
-
export { BatchConversionOptions, BatchConversionResult, CliOptions, ConversionError, ConversionOptions, ConversionResult, Framework, ReactConversionOptions, VueConversionOptions, convertToReact, convertToVue, createSvgoConfig, formatComponentName, optimizeSvg, pascalCase, readSvgDirectory, readSvgFile, sanitizeComponentName, svgToComponentName, writeComponentFile, writeSvgFile };
|
|
158
|
+
export { type BatchConversionOptions, type BatchConversionResult, type CliOptions, type ConversionError, type ConversionOptions, type ConversionResult, type Framework, type ReactConversionOptions, type VueConversionOptions, convertToReact, convertToVue, createSvgoConfig, formatComponentName, optimizeSvg, pascalCase, readSvgDirectory, readSvgFile, sanitizeComponentName, svgToComponentName, writeComponentFile, writeSvgFile };
|
package/dist/index.d.ts
CHANGED
|
@@ -13,10 +13,22 @@ interface ReactConversionOptions extends ConversionOptions {
|
|
|
13
13
|
ref?: boolean;
|
|
14
14
|
titleProp?: boolean;
|
|
15
15
|
descProp?: boolean;
|
|
16
|
+
icon?: boolean;
|
|
17
|
+
dimensions?: boolean;
|
|
18
|
+
replaceAttrValues?: Record<string, string>;
|
|
19
|
+
svgProps?: Record<string, string>;
|
|
20
|
+
expandProps?: boolean | 'start' | 'end';
|
|
21
|
+
nativeProps?: boolean;
|
|
22
|
+
ariaLabelledBy?: boolean;
|
|
23
|
+
ariaHidden?: boolean;
|
|
24
|
+
role?: 'img' | 'graphics-document' | 'graphics-symbol' | 'presentation';
|
|
16
25
|
}
|
|
17
26
|
interface VueConversionOptions extends ConversionOptions {
|
|
18
27
|
compositionApi?: boolean;
|
|
19
28
|
scriptSetup?: boolean;
|
|
29
|
+
props?: boolean;
|
|
30
|
+
dimensions?: boolean;
|
|
31
|
+
replaceAttrValues?: Record<string, string>;
|
|
20
32
|
}
|
|
21
33
|
interface ConversionResult {
|
|
22
34
|
code: string;
|
|
@@ -60,19 +72,13 @@ interface CliOptions {
|
|
|
60
72
|
|
|
61
73
|
/**
|
|
62
74
|
* Convert SVG to React component
|
|
63
|
-
* @param svgContent - SVG content string
|
|
64
|
-
* @param options - Conversion options
|
|
65
|
-
* @returns Conversion result with React component code
|
|
66
75
|
*/
|
|
67
76
|
declare function convertToReact(svgContent: string, options?: ReactConversionOptions): Promise<ConversionResult>;
|
|
68
77
|
|
|
69
78
|
/**
|
|
70
|
-
* Convert SVG to Vue
|
|
71
|
-
* @param svgContent - SVG content string
|
|
72
|
-
* @param options - Conversion options
|
|
73
|
-
* @returns Conversion result with Vue component code
|
|
79
|
+
* Convert SVG to Vue component
|
|
74
80
|
*/
|
|
75
|
-
declare function convertToVue(svgContent: string, options?: VueConversionOptions): ConversionResult
|
|
81
|
+
declare function convertToVue(svgContent: string, options?: VueConversionOptions): Promise<ConversionResult>;
|
|
76
82
|
|
|
77
83
|
/**
|
|
78
84
|
* Optimize SVG content using SVGO
|
|
@@ -149,4 +155,4 @@ declare function pascalCase(str: string): string;
|
|
|
149
155
|
*/
|
|
150
156
|
declare function formatComponentName(name: string, prefix?: string, suffix?: string): string;
|
|
151
157
|
|
|
152
|
-
export { BatchConversionOptions, BatchConversionResult, CliOptions, ConversionError, ConversionOptions, ConversionResult, Framework, ReactConversionOptions, VueConversionOptions, convertToReact, convertToVue, createSvgoConfig, formatComponentName, optimizeSvg, pascalCase, readSvgDirectory, readSvgFile, sanitizeComponentName, svgToComponentName, writeComponentFile, writeSvgFile };
|
|
158
|
+
export { type BatchConversionOptions, type BatchConversionResult, type CliOptions, type ConversionError, type ConversionOptions, type ConversionResult, type Framework, type ReactConversionOptions, type VueConversionOptions, convertToReact, convertToVue, createSvgoConfig, formatComponentName, optimizeSvg, pascalCase, readSvgDirectory, readSvgFile, sanitizeComponentName, svgToComponentName, writeComponentFile, writeSvgFile };
|
package/dist/index.js
CHANGED
|
@@ -1,101 +1,23 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
var core = require('@svgr/core');
|
|
4
|
-
var svgo = require('svgo');
|
|
5
|
-
var promises = require('fs/promises');
|
|
6
|
-
var path = require('path');
|
|
7
|
-
var fs = require('fs');
|
|
8
|
-
|
|
9
|
-
var j={plugins:[{name:"preset-default",params:{overrides:{removeViewBox:!1,removeTitle:!1,removeDesc:!1,removeUselessStrokeAndFill:!1,convertColors:{currentColor:!0,names2hex:!0,rgb2hex:!0,shorthex:!0,shortname:!0}}}},"removeDimensions","cleanupNumericValues"]};function g(e,t=j){try{return svgo.optimize(e,t).data}catch(r){throw new Error(`Failed to optimize SVG: ${r}`)}}function B(e){let t=[{name:"preset-default",params:{overrides:{removeViewBox:!e.removeViewBox,removeTitle:!e.removeTitle,removeDesc:!e.removeDesc,removeUselessStrokeAndFill:!e.preserveClasses,convertColors:e.preserveColors?!1:{currentColor:!0,names2hex:!0,rgb2hex:!0,shorthex:!0,shortname:!0}}}},"cleanupNumericValues"];return e.removeDimensions!==!1&&t.push("removeDimensions"),{plugins:t}}function _(e){let t=e.replace(/\.svg$/i,"");return t=k(t),u(t)}function k(e){let t=e,r=t.match(/^([^,]+),\s*Type=([^,]+)/i);if(r)return t=`${r[1]} ${r[2]}`,t;let n=t.match(/Size=(\w+)/i),s=t.match(/Color=(\w+)/i),i=t.match(/Type=(\w+)/i);if(n||s||i){let o=[];if(i&&o.push(i[1]),s&&o.push(s[1]),n&&o.push(n[1]),o.length>0)return t=o.join(" "),t}return t=t.replace(/\b\w+=\w+\b/g,"").replace(/,\s*/g," ").replace(/[=]/g," ").replace(/\s+/g," ").trim(),(!t||t.length<2)&&(t=e),t}function M(e){return u(e.replace(/[^a-zA-Z0-9]/g," "))}function u(e){return e.replace(/[^a-zA-Z0-9\s-_]/g," ").split(/[\s-_]+/).filter(t=>t.length>0).map(t=>t.charAt(0).toUpperCase()+t.slice(1).toLowerCase()).join("")}function v(e,t,r){let n=t?u(t):"",s=r?u(r):"",i=u(e);return `${n}${i}${s}`}async function J(e){try{return await promises.readFile(e,"utf-8")}catch(t){throw new Error(`Failed to read SVG file: ${e}. ${t}`)}}async function K(e,t){try{await D(path.dirname(e)),await promises.writeFile(e,t,"utf-8");}catch(r){throw new Error(`Failed to write SVG file: ${e}. ${r}`)}}async function Q(e,t){try{await D(path.dirname(e)),await promises.writeFile(e,t,"utf-8");}catch(r){throw new Error(`Failed to write component file: ${e}. ${r}`)}}async function T(e,t=!1){try{let r=await promises.readdir(e),n=[];for(let s of r){let i=path.join(e,s),o=await promises.stat(i);if(o.isDirectory()&&t){let c=await T(i,t);n.push(...c);}else o.isFile()&&path.extname(s).toLowerCase()===".svg"&&n.push(i);}return n}catch(r){throw new Error(`Failed to read directory: ${e}. ${r}`)}}async function D(e){fs.existsSync(e)||await promises.mkdir(e,{recursive:!0});}function C(e,t=!0){return e==="react"?t?".tsx":".jsx":".vue"}function $(e,t,r){return `${t}${r}`}async function Y(e,t={}){let{name:r,prefix:n,suffix:s,optimize:i=!0,typescript:o=!0,memo:c=!0,ref:l=!0,titleProp:m=!0,descProp:h=!0}=t;try{let p=e;i&&(p=g(e));let a=v(r||"icon",n,s),d=await core.transform(p,{typescript:o,memo:c,ref:l,titleProp:m,descProp:h,svgProps:{className:"{className}"}},{componentName:a}),x=o?`import { SVGProps } from 'react';
|
|
10
|
-
`:"",w=c?`import { memo } from 'react';
|
|
11
|
-
`:"",y=l?`import { forwardRef } from 'react';
|
|
12
|
-
`:"",N=`
|
|
13
|
-
export default ${a};
|
|
14
|
-
export { ${a} };`,b=o?"SVGProps<SVGSVGElement> & { className?: string; }":"",z=o?`props: ${b}`:"props",E=c?"memo(":"",O=l?`forwardRef<SVGSVGElement, ${b}>(`:"";d=`${x}${w}${y}
|
|
15
|
-
const ${a} = ${E}${O}(${z}) => {
|
|
16
|
-
return ${d};
|
|
17
|
-
}${`${l?")":""}${c?")":""}`};
|
|
18
|
-
|
|
19
|
-
${a}.displayName = "${a}";
|
|
20
|
-
${N}`;let P=C("react",o),G=$("icon.svg",a,P);return {code:d,filename:G,componentName:a}}catch(p){throw new Error(`Failed to convert SVG to React: ${p}`)}}function ee(e,t={}){let{name:r,prefix:n,suffix:s,optimize:i=!0,typescript:o=!0,compositionApi:c=!0,scriptSetup:l=!0}=t;try{let m=e;i&&(m=g(e));let p=v(r||"icon",n,s),S=m.replace(/<\?xml[^>]*\?>\s*/,"").replace(/xmlns="[^"]*"/g,"").replace(/width="[^"]*"/g,"").replace(/height="[^"]*"/g,"").replace(/<svg/,'<svg v-bind="$attrs"').replace(/class="([^"]*)"/g,'class="$1"').replace(/currentColor/g,"currentColor"),a=l?te(o,p):re(p,o),F=`<template>
|
|
21
|
-
${S}
|
|
22
|
-
</template>`,x=`${a}
|
|
23
|
-
|
|
24
|
-
${F}
|
|
25
|
-
|
|
26
|
-
<style scoped>
|
|
27
|
-
/* Add component-specific styles here */
|
|
28
|
-
</style>`,w=C("vue",o),y=$("icon.svg",p,w);return {code:x,filename:y,componentName:p}}catch(m){throw new Error(`Failed to convert SVG to Vue: ${m}`)}}function te(e,t){return `<script setup${e?' lang="ts"':""}>${e?`
|
|
29
|
-
interface Props {
|
|
30
|
-
class?: string;
|
|
31
|
-
style?: string | Record<string, any>;
|
|
32
|
-
}`:""}${e?`
|
|
33
|
-
const props = withDefaults(defineProps<Props>(), {
|
|
34
|
-
class: '',
|
|
35
|
-
style: undefined,
|
|
36
|
-
});`:`
|
|
37
|
-
const props = withDefaults(defineProps(), {
|
|
38
|
-
class: '',
|
|
39
|
-
style: undefined,
|
|
40
|
-
});`}
|
|
41
|
-
|
|
42
|
-
// Component name for debugging
|
|
43
|
-
const __name = '${t}';
|
|
44
|
-
</script>`}function re(e,t){let r=t?' lang="ts"':"",n=t?`
|
|
1
|
+
'use strict';var promises=require('fs/promises'),path=require('path'),fs=require('fs'),core=require('@svgr/core'),svgo=require('svgo');var d=Object.defineProperty;var I=Object.getOwnPropertyDescriptor;var L=Object.getOwnPropertyNames;var k=Object.prototype.hasOwnProperty;var R=(e,r)=>()=>(e&&(r=e(e=0)),r);var _=(e,r)=>{for(var t in r)d(e,t,{get:r[t],enumerable:true});},q=(e,r,t,o)=>{if(r&&typeof r=="object"||typeof r=="function")for(let n of L(r))!k.call(e,n)&&n!==t&&d(e,n,{get:()=>r[n],enumerable:!(o=I(r,n))||o.enumerable});return e};var M=e=>q(d({},"__esModule",{value:true}),e);var a=R(()=>{});var T={};_(T,{ensureDirectoryExists:()=>h,getComponentFilename:()=>ie,getFileExtension:()=>se,readSvgDirectory:()=>x,readSvgFile:()=>O,writeComponentFile:()=>D,writeSvgFile:()=>P});async function O(e){try{return await promises.readFile(e,"utf-8")}catch(r){throw new Error(`Failed to read SVG file: ${e}. ${r}`)}}async function P(e,r){try{await h(path.dirname(e)),await promises.writeFile(e,r,"utf-8");}catch(t){throw new Error(`Failed to write SVG file: ${e}. ${t}`)}}async function D(e,r){try{await h(path.dirname(e)),await promises.writeFile(e,r,"utf-8");}catch(t){throw new Error(`Failed to write component file: ${e}. ${t}`)}}async function x(e,r=false){try{let t=await promises.readdir(e),o=[];for(let n of t){let s=path.join(e,n),i=await promises.stat(s);if(i.isDirectory()&&r){let p=await x(s,r);o.push(...p);}else i.isFile()&&path.extname(n).toLowerCase()===".svg"&&o.push(s);}return o}catch(t){throw new Error(`Failed to read directory: ${e}. ${t}`)}}async function h(e){fs.existsSync(e)||await promises.mkdir(e,{recursive:true});}function se(e,r=true){return e==="react"?r?".tsx":".jsx":".vue"}function ie(e,r,t){return `${r}${t}`}var w=R(()=>{a();});a();a();a();a();var H={plugins:[{name:"preset-default",params:{overrides:{removeViewBox:false,removeTitle:false,removeDesc:false,removeUselessStrokeAndFill:false,convertColors:{currentColor:true,names2hex:true,rgb2hex:true,shorthex:true,shortname:true}}}},"removeDimensions","cleanupNumericValues"]};function C(e,r=H){try{return svgo.optimize(e,r).data}catch(t){throw new Error(`Failed to optimize SVG: ${t}`)}}function J(e){let r=[{name:"preset-default",params:{overrides:{removeViewBox:!e.removeViewBox,removeTitle:!e.removeTitle,removeDesc:!e.removeDesc,removeUselessStrokeAndFill:!e.preserveClasses,convertColors:e.preserveColors?false:{currentColor:true,names2hex:true,rgb2hex:true,shorthex:true,shortname:true}}}},"cleanupNumericValues"];return e.removeDimensions!==false&&r.push("removeDimensions"),{plugins:r}}a();function K(e){let r=e.replace(/\.svg$/i,"");return r=Q(r),u(r)}function Q(e){let r=e,t=r.match(/^([^,]+),\s*Type=([^,]+)/i);if(t)return r=`${t[1]} ${t[2]}`,r;let o=r.match(/Size=(\w+)/i),n=r.match(/Color=(\w+)/i),s=r.match(/Type=(\w+)/i);if(o||n||s){let i=[];if(s&&i.push(s[1]),n&&i.push(n[1]),o&&i.push(o[1]),i.length>0)return r=i.join(" "),r}return r=r.replace(/\b\w+=\w+\b/g,"").replace(/,\s*/g," ").replace(/[=]/g," ").replace(/\s+/g," ").trim(),(!r||r.length<2)&&(r=e),r}function W(e){return u(e.replace(/[^a-zA-Z0-9]/g," "))}function u(e){return e.replace(/[^a-zA-Z0-9\s-_]/g," ").split(/[\s-_]+/).filter(r=>r.length>0).map(r=>r.charAt(0).toUpperCase()+r.slice(1).toLowerCase()).join("")}function f(e,r,t){let o=r?u(r):"",n=t?u(t):"",s=u(e);return `${o}${s}${n}`}var g=class{async processSvg(r,t){let{optimize:o=true}=t;return o?C(r):r}generateComponentName(r){let{name:t,prefix:o,suffix:n}=r;return f(t||"Icon",o,n)}generateFilename(r,t,o=true){try{let{getFileExtension:n,getComponentFilename:s}=(w(),M(T)),i=n(t,o);return s("icon.svg",r,i)}catch{return `${r}${{react:o?".tsx":".jsx",vue:".vue"}[t]}`}}};a();function U(e){let{typescript:r=true,memo:t=true,ref:o=true,titleProp:n=true,descProp:s=true,icon:i=true,dimensions:p=false,replaceAttrValues:c={"#000":"currentColor","#000000":"currentColor"},svgProps:l={},expandProps:v=false,nativeProps:E=true,ariaLabelledBy:j=false,ariaHidden:B=false,role:G="img"}=e,b={typescript:r,memo:t,ref:o,titleProp:n,descProp:s,icon:i,dimensions:p,expandProps:v,svgProps:{className:"{className}",...o&&{ref:"{ref}"},...E&&{width:"{width}",height:"{height}",style:"{style}"},...j&&n&&s&&{"aria-labelledby":"{titleId} {descId}"},...B&&{"aria-hidden":"true"},role:G,...l},replaceAttrValues:c,plugins:["@svgr/plugin-svgo","@svgr/plugin-jsx","@svgr/plugin-prettier"]};return b.svgoConfig={plugins:[{name:"preset-default",params:{overrides:{removeViewBox:false,removeTitle:!n,removeDesc:!s,removeUselessStrokeAndFill:false,removeUnusedNS:false,removeUselessDefs:false,convertShapeToPath:false,mergePaths:false,convertColors:false}}},...i&&!p?[{name:"removeAttrs",params:{attrs:["width","height"]}}]:[],...i?["cleanupNumericValues"]:[]]},b}function A(e,r,t){let o=e;return y(o)&&(o=$(o,r)),o}function y(e){return ["linearGradient","radialGradient","pattern","mask","filter","clipPath","marker","symbol","use"].some(t=>e.includes(`<${t}`)||e.includes(`</${t}`))}function $(e,r){let t=`${r.toLowerCase()}_`;return e=e.replace(/id="([^"]+)"/g,`id="${t}$1"`),e=e.replace(/url\(#([^)]+)\)/g,`url(#${t}$1)`),e=e.replace(/href="#([^"]+)"/g,`href="#${t}$1"`),e}var S=class extends g{async convert(r,t={}){try{let o=this.generateComponentName(t),n=await this.processSvg(r,t);y(n)&&(n=$(n,o));let s=U(t),i=await core.transform(n,s,{componentName:o}),p=A(i,o,t),c=this.generateFilename(o,"react",t.typescript??!0);return {code:p,filename:c,componentName:o}}catch(o){throw new Error(`Failed to convert SVG to React: ${o}`)}}};async function ce(e,r={}){return new S().convert(e,r)}a();a();function z(e,r){let{name:t,prefix:o,suffix:n,props:s=true,replaceAttrValues:i={"#000":"currentColor","#000000":"currentColor"}}=r,c=f(t||"Icon",o,n),l=le(e);return l=pe(l,i),me(l)&&(l=ue(l,c)),l=ge(l,s),{code:fe(l,c,r),componentName:c}}function le(e){return e.replace(/<\?xml[^>]*\?>\s*/,"").replace(/<!--[\s\S]*?-->/g,"").replace(/xmlns="[^"]*"/g,"").trim()}function pe(e,r){let t=e;for(let[o,n]of Object.entries(r)){let s=new RegExp(o.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),"g");t=t.replace(s,n);}return t}function me(e){return ["linearGradient","radialGradient","pattern","mask","filter","clipPath","marker","symbol","use"].some(t=>e.includes(`<${t}`)||e.includes(`</${t}`))}function ue(e,r){let t=`${r.toLowerCase()}_`;return e=e.replace(/id="([^"]+)"/g,`id="${t}$1"`),e=e.replace(/url\(#([^)]+)\)/g,`url(#${t}$1)`),e=e.replace(/href="#([^"]+)"/g,`href="#${t}$1"`),e}function ge(e,r){return r?e.replace("<svg",'<svg :class="className" :style="style" v-bind="$attrs"'):e}function fe(e,r,t){let{typescript:o,compositionApi:n,props:s}=t,c=`<script${o?' lang="ts"':""}${n?" setup":""}>`;n?s&&(c+=`
|
|
45
2
|
interface Props {
|
|
46
|
-
|
|
47
|
-
style?:
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
|
|
3
|
+
className?: string;
|
|
4
|
+
style?: Record<string, any>;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
defineProps<Props>();
|
|
8
|
+
`):c+=`
|
|
9
|
+
export default {
|
|
10
|
+
name: '${r}',
|
|
51
11
|
props: {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
default: '',
|
|
55
|
-
},
|
|
56
|
-
style: {
|
|
57
|
-
type: [String, Object],
|
|
58
|
-
default: undefined,
|
|
59
|
-
},
|
|
12
|
+
className: String,
|
|
13
|
+
style: Object,
|
|
60
14
|
},
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
props: {
|
|
71
|
-
class: {
|
|
72
|
-
type: String,
|
|
73
|
-
default: '',
|
|
74
|
-
},
|
|
75
|
-
style: {
|
|
76
|
-
type: [String, Object],
|
|
77
|
-
default: undefined,
|
|
78
|
-
},
|
|
79
|
-
},
|
|
80
|
-
setup(props) {
|
|
81
|
-
return {};
|
|
82
|
-
},
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
export default ${e};
|
|
86
|
-
export { ${e} };`;return `<script${r}>
|
|
87
|
-
import { defineComponent } from 'vue';${n}${s}
|
|
88
|
-
</script>`}
|
|
89
|
-
|
|
90
|
-
exports.convertToReact = Y;
|
|
91
|
-
exports.convertToVue = ee;
|
|
92
|
-
exports.createSvgoConfig = B;
|
|
93
|
-
exports.formatComponentName = v;
|
|
94
|
-
exports.optimizeSvg = g;
|
|
95
|
-
exports.pascalCase = u;
|
|
96
|
-
exports.readSvgDirectory = T;
|
|
97
|
-
exports.readSvgFile = J;
|
|
98
|
-
exports.sanitizeComponentName = M;
|
|
99
|
-
exports.svgToComponentName = _;
|
|
100
|
-
exports.writeComponentFile = Q;
|
|
101
|
-
exports.writeSvgFile = K;
|
|
15
|
+
};
|
|
16
|
+
`,c+="</script>";let l=`
|
|
17
|
+
<template>
|
|
18
|
+
${e}
|
|
19
|
+
</template>`;return [c,l,`
|
|
20
|
+
<style scoped>
|
|
21
|
+
/* Component styles */
|
|
22
|
+
</style>`].filter(Boolean).join(`
|
|
23
|
+
`)}var F=class extends g{async convert(r,t={}){try{let o=await this.processSvg(r,t),{code:n,componentName:s}=z(o,t),i=this.generateFilename(s,"vue",t.typescript??!0);return {code:n,filename:i,componentName:s}}catch(o){throw new Error(`Failed to convert SVG to Vue: ${o}`)}}};async function ve(e,r={}){return new F().convert(e,r)}w();exports.convertToReact=ce;exports.convertToVue=ve;exports.createSvgoConfig=J;exports.formatComponentName=f;exports.optimizeSvg=C;exports.pascalCase=u;exports.readSvgDirectory=x;exports.readSvgFile=O;exports.sanitizeComponentName=W;exports.svgToComponentName=K;exports.writeComponentFile=D;exports.writeSvgFile=P;
|
package/dist/index.mjs
CHANGED
|
@@ -1,88 +1,23 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { optimize } from 'svgo';
|
|
3
|
-
import { readFile, writeFile, readdir, stat, mkdir } from 'fs/promises';
|
|
4
|
-
import { dirname, join, extname } from 'path';
|
|
5
|
-
import { existsSync } from 'fs';
|
|
6
|
-
|
|
7
|
-
var B={plugins:[{name:"preset-default",params:{overrides:{removeViewBox:!1,removeTitle:!1,removeDesc:!1,removeUselessStrokeAndFill:!1,convertColors:{currentColor:!0,names2hex:!0,rgb2hex:!0,shorthex:!0,shortname:!0}}}},"removeDimensions","cleanupNumericValues"]};function v(e,t=B){try{return optimize(e,t).data}catch(r){throw new Error(`Failed to optimize SVG: ${r}`)}}function _(e){let t=[{name:"preset-default",params:{overrides:{removeViewBox:!e.removeViewBox,removeTitle:!e.removeTitle,removeDesc:!e.removeDesc,removeUselessStrokeAndFill:!e.preserveClasses,convertColors:e.preserveColors?!1:{currentColor:!0,names2hex:!0,rgb2hex:!0,shorthex:!0,shortname:!0}}}},"cleanupNumericValues"];return e.removeDimensions!==!1&&t.push("removeDimensions"),{plugins:t}}function k(e){let t=e.replace(/\.svg$/i,"");return t=M(t),u(t)}function M(e){let t=e,r=t.match(/^([^,]+),\s*Type=([^,]+)/i);if(r)return t=`${r[1]} ${r[2]}`,t;let n=t.match(/Size=(\w+)/i),s=t.match(/Color=(\w+)/i),i=t.match(/Type=(\w+)/i);if(n||s||i){let o=[];if(i&&o.push(i[1]),s&&o.push(s[1]),n&&o.push(n[1]),o.length>0)return t=o.join(" "),t}return t=t.replace(/\b\w+=\w+\b/g,"").replace(/,\s*/g," ").replace(/[=]/g," ").replace(/\s+/g," ").trim(),(!t||t.length<2)&&(t=e),t}function I(e){return u(e.replace(/[^a-zA-Z0-9]/g," "))}function u(e){return e.replace(/[^a-zA-Z0-9\s-_]/g," ").split(/[\s-_]+/).filter(t=>t.length>0).map(t=>t.charAt(0).toUpperCase()+t.slice(1).toLowerCase()).join("")}function d(e,t,r){let n=t?u(t):"",s=r?u(r):"",i=u(e);return `${n}${i}${s}`}async function K(e){try{return await readFile(e,"utf-8")}catch(t){throw new Error(`Failed to read SVG file: ${e}. ${t}`)}}async function Q(e,t){try{await N(dirname(e)),await writeFile(e,t,"utf-8");}catch(r){throw new Error(`Failed to write SVG file: ${e}. ${r}`)}}async function X(e,t){try{await N(dirname(e)),await writeFile(e,t,"utf-8");}catch(r){throw new Error(`Failed to write component file: ${e}. ${r}`)}}async function D(e,t=!1){try{let r=await readdir(e),n=[];for(let s of r){let i=join(e,s),o=await stat(i);if(o.isDirectory()&&t){let c=await D(i,t);n.push(...c);}else o.isFile()&&extname(s).toLowerCase()===".svg"&&n.push(i);}return n}catch(r){throw new Error(`Failed to read directory: ${e}. ${r}`)}}async function N(e){existsSync(e)||await mkdir(e,{recursive:!0});}function $(e,t=!0){return e==="react"?t?".tsx":".jsx":".vue"}function x(e,t,r){return `${t}${r}`}async function ee(e,t={}){let{name:r,prefix:n,suffix:s,optimize:i=!0,typescript:o=!0,memo:c=!0,ref:l=!0,titleProp:m=!0,descProp:S=!0}=t;try{let p=e;i&&(p=v(e));let a=d(r||"icon",n,s),C=await transform(p,{typescript:o,memo:c,ref:l,titleProp:m,descProp:S,svgProps:{className:"{className}"}},{componentName:a}),w=o?`import { SVGProps } from 'react';
|
|
8
|
-
`:"",y=c?`import { memo } from 'react';
|
|
9
|
-
`:"",h=l?`import { forwardRef } from 'react';
|
|
10
|
-
`:"",z=`
|
|
11
|
-
export default ${a};
|
|
12
|
-
export { ${a} };`,V=o?"SVGProps<SVGSVGElement> & { className?: string; }":"",E=o?`props: ${V}`:"props",O=c?"memo(":"",P=l?`forwardRef<SVGSVGElement, ${V}>(`:"";C=`${w}${y}${h}
|
|
13
|
-
const ${a} = ${O}${P}(${E}) => {
|
|
14
|
-
return ${C};
|
|
15
|
-
}${`${l?")":""}${c?")":""}`};
|
|
16
|
-
|
|
17
|
-
${a}.displayName = "${a}";
|
|
18
|
-
${z}`;let G=$("react",o),A=x("icon.svg",a,G);return {code:C,filename:A,componentName:a}}catch(p){throw new Error(`Failed to convert SVG to React: ${p}`)}}function te(e,t={}){let{name:r,prefix:n,suffix:s,optimize:i=!0,typescript:o=!0,compositionApi:c=!0,scriptSetup:l=!0}=t;try{let m=e;i&&(m=v(e));let p=d(r||"icon",n,s),F=m.replace(/<\?xml[^>]*\?>\s*/,"").replace(/xmlns="[^"]*"/g,"").replace(/width="[^"]*"/g,"").replace(/height="[^"]*"/g,"").replace(/<svg/,'<svg v-bind="$attrs"').replace(/class="([^"]*)"/g,'class="$1"').replace(/currentColor/g,"currentColor"),a=l?re(o,p):oe(p,o),b=`<template>
|
|
19
|
-
${F}
|
|
20
|
-
</template>`,w=`${a}
|
|
21
|
-
|
|
22
|
-
${b}
|
|
23
|
-
|
|
24
|
-
<style scoped>
|
|
25
|
-
/* Add component-specific styles here */
|
|
26
|
-
</style>`,y=$("vue",o),h=x("icon.svg",p,y);return {code:w,filename:h,componentName:p}}catch(m){throw new Error(`Failed to convert SVG to Vue: ${m}`)}}function re(e,t){return `<script setup${e?' lang="ts"':""}>${e?`
|
|
27
|
-
interface Props {
|
|
28
|
-
class?: string;
|
|
29
|
-
style?: string | Record<string, any>;
|
|
30
|
-
}`:""}${e?`
|
|
31
|
-
const props = withDefaults(defineProps<Props>(), {
|
|
32
|
-
class: '',
|
|
33
|
-
style: undefined,
|
|
34
|
-
});`:`
|
|
35
|
-
const props = withDefaults(defineProps(), {
|
|
36
|
-
class: '',
|
|
37
|
-
style: undefined,
|
|
38
|
-
});`}
|
|
39
|
-
|
|
40
|
-
// Component name for debugging
|
|
41
|
-
const __name = '${t}';
|
|
42
|
-
</script>`}function oe(e,t){let r=t?' lang="ts"':"",n=t?`
|
|
1
|
+
import {dirname,join,extname}from'path';import'url';import {readFile,writeFile,readdir,stat,mkdir}from'fs/promises';import {existsSync}from'fs';import {transform}from'@svgr/core';import {optimize}from'svgo';var d=Object.defineProperty;var k=Object.getOwnPropertyDescriptor;var L=Object.getOwnPropertyNames;var I=Object.prototype.hasOwnProperty;var N=(e,r)=>()=>(e&&(r=e(e=0)),r);var q=(e,r)=>{for(var t in r)d(e,t,{get:r[t],enumerable:true});},M=(e,r,t,o)=>{if(r&&typeof r=="object"||typeof r=="function")for(let n of L(r))!I.call(e,n)&&n!==t&&d(e,n,{get:()=>r[n],enumerable:!(o=k(r,n))||o.enumerable});return e};var Z=e=>M(d({},"__esModule",{value:true}),e);var a=N(()=>{});var A={};q(A,{ensureDirectoryExists:()=>w,getComponentFilename:()=>ae,getFileExtension:()=>ie,readSvgDirectory:()=>h,readSvgFile:()=>O,writeComponentFile:()=>T,writeSvgFile:()=>D});async function O(e){try{return await readFile(e,"utf-8")}catch(r){throw new Error(`Failed to read SVG file: ${e}. ${r}`)}}async function D(e,r){try{await w(dirname(e)),await writeFile(e,r,"utf-8");}catch(t){throw new Error(`Failed to write SVG file: ${e}. ${t}`)}}async function T(e,r){try{await w(dirname(e)),await writeFile(e,r,"utf-8");}catch(t){throw new Error(`Failed to write component file: ${e}. ${t}`)}}async function h(e,r=false){try{let t=await readdir(e),o=[];for(let n of t){let s=join(e,n),i=await stat(s);if(i.isDirectory()&&r){let p=await h(s,r);o.push(...p);}else i.isFile()&&extname(n).toLowerCase()===".svg"&&o.push(s);}return o}catch(t){throw new Error(`Failed to read directory: ${e}. ${t}`)}}async function w(e){existsSync(e)||await mkdir(e,{recursive:true});}function ie(e,r=true){return e==="react"?r?".tsx":".jsx":".vue"}function ae(e,r,t){return `${r}${t}`}var y=N(()=>{a();});a();a();a();a();var J={plugins:[{name:"preset-default",params:{overrides:{removeViewBox:false,removeTitle:false,removeDesc:false,removeUselessStrokeAndFill:false,convertColors:{currentColor:true,names2hex:true,rgb2hex:true,shorthex:true,shortname:true}}}},"removeDimensions","cleanupNumericValues"]};function x(e,r=J){try{return optimize(e,r).data}catch(t){throw new Error(`Failed to optimize SVG: ${t}`)}}function K(e){let r=[{name:"preset-default",params:{overrides:{removeViewBox:!e.removeViewBox,removeTitle:!e.removeTitle,removeDesc:!e.removeDesc,removeUselessStrokeAndFill:!e.preserveClasses,convertColors:e.preserveColors?false:{currentColor:true,names2hex:true,rgb2hex:true,shorthex:true,shortname:true}}}},"cleanupNumericValues"];return e.removeDimensions!==false&&r.push("removeDimensions"),{plugins:r}}a();function Q(e){let r=e.replace(/\.svg$/i,"");return r=W(r),g(r)}function W(e){let r=e,t=r.match(/^([^,]+),\s*Type=([^,]+)/i);if(t)return r=`${t[1]} ${t[2]}`,r;let o=r.match(/Size=(\w+)/i),n=r.match(/Color=(\w+)/i),s=r.match(/Type=(\w+)/i);if(o||n||s){let i=[];if(s&&i.push(s[1]),n&&i.push(n[1]),o&&i.push(o[1]),i.length>0)return r=i.join(" "),r}return r=r.replace(/\b\w+=\w+\b/g,"").replace(/,\s*/g," ").replace(/[=]/g," ").replace(/\s+/g," ").trim(),(!r||r.length<2)&&(r=e),r}function X(e){return g(e.replace(/[^a-zA-Z0-9]/g," "))}function g(e){return e.replace(/[^a-zA-Z0-9\s-_]/g," ").split(/[\s-_]+/).filter(r=>r.length>0).map(r=>r.charAt(0).toUpperCase()+r.slice(1).toLowerCase()).join("")}function v(e,r,t){let o=r?g(r):"",n=t?g(t):"",s=g(e);return `${o}${s}${n}`}var f=class{async processSvg(r,t){let{optimize:o=true}=t;return o?x(r):r}generateComponentName(r){let{name:t,prefix:o,suffix:n}=r;return v(t||"Icon",o,n)}generateFilename(r,t,o=true){try{let{getFileExtension:n,getComponentFilename:s}=(y(),Z(A)),i=n(t,o);return s("icon.svg",r,i)}catch{return `${r}${{react:o?".tsx":".jsx",vue:".vue"}[t]}`}}};a();function z(e){let{typescript:r=true,memo:t=true,ref:o=true,titleProp:n=true,descProp:s=true,icon:i=true,dimensions:p=false,replaceAttrValues:c={"#000":"currentColor","#000000":"currentColor"},svgProps:l={},expandProps:C=false,nativeProps:B=true,ariaLabelledBy:U=false,ariaHidden:_=false,role:G="img"}=e,V={typescript:r,memo:t,ref:o,titleProp:n,descProp:s,icon:i,dimensions:p,expandProps:C,svgProps:{className:"{className}",...o&&{ref:"{ref}"},...B&&{width:"{width}",height:"{height}",style:"{style}"},...U&&n&&s&&{"aria-labelledby":"{titleId} {descId}"},..._&&{"aria-hidden":"true"},role:G,...l},replaceAttrValues:c,plugins:["@svgr/plugin-svgo","@svgr/plugin-jsx","@svgr/plugin-prettier"]};return V.svgoConfig={plugins:[{name:"preset-default",params:{overrides:{removeViewBox:false,removeTitle:!n,removeDesc:!s,removeUselessStrokeAndFill:false,removeUnusedNS:false,removeUselessDefs:false,convertShapeToPath:false,mergePaths:false,convertColors:false}}},...i&&!p?[{name:"removeAttrs",params:{attrs:["width","height"]}}]:[],...i?["cleanupNumericValues"]:[]]},V}function E(e,r,t){let o=e;return $(o)&&(o=F(o,r)),o}function $(e){return ["linearGradient","radialGradient","pattern","mask","filter","clipPath","marker","symbol","use"].some(t=>e.includes(`<${t}`)||e.includes(`</${t}`))}function F(e,r){let t=`${r.toLowerCase()}_`;return e=e.replace(/id="([^"]+)"/g,`id="${t}$1"`),e=e.replace(/url\(#([^)]+)\)/g,`url(#${t}$1)`),e=e.replace(/href="#([^"]+)"/g,`href="#${t}$1"`),e}var S=class extends f{async convert(r,t={}){try{let o=this.generateComponentName(t),n=await this.processSvg(r,t);$(n)&&(n=F(n,o));let s=z(t),i=await transform(n,s,{componentName:o}),p=E(i,o,t),c=this.generateFilename(o,"react",t.typescript??!0);return {code:p,filename:c,componentName:o}}catch(o){throw new Error(`Failed to convert SVG to React: ${o}`)}}};async function le(e,r={}){return new S().convert(e,r)}a();a();function j(e,r){let{name:t,prefix:o,suffix:n,props:s=true,replaceAttrValues:i={"#000":"currentColor","#000000":"currentColor"}}=r,c=v(t||"Icon",o,n),l=pe(e);return l=me(l,i),ue(l)&&(l=ge(l,c)),l=fe(l,s),{code:ve(l,c,r),componentName:c}}function pe(e){return e.replace(/<\?xml[^>]*\?>\s*/,"").replace(/<!--[\s\S]*?-->/g,"").replace(/xmlns="[^"]*"/g,"").trim()}function me(e,r){let t=e;for(let[o,n]of Object.entries(r)){let s=new RegExp(o.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),"g");t=t.replace(s,n);}return t}function ue(e){return ["linearGradient","radialGradient","pattern","mask","filter","clipPath","marker","symbol","use"].some(t=>e.includes(`<${t}`)||e.includes(`</${t}`))}function ge(e,r){let t=`${r.toLowerCase()}_`;return e=e.replace(/id="([^"]+)"/g,`id="${t}$1"`),e=e.replace(/url\(#([^)]+)\)/g,`url(#${t}$1)`),e=e.replace(/href="#([^"]+)"/g,`href="#${t}$1"`),e}function fe(e,r){return r?e.replace("<svg",'<svg :class="className" :style="style" v-bind="$attrs"'):e}function ve(e,r,t){let{typescript:o,compositionApi:n,props:s}=t,c=`<script${o?' lang="ts"':""}${n?" setup":""}>`;n?s&&(c+=`
|
|
43
2
|
interface Props {
|
|
44
|
-
|
|
45
|
-
style?:
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
|
|
3
|
+
className?: string;
|
|
4
|
+
style?: Record<string, any>;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
defineProps<Props>();
|
|
8
|
+
`):c+=`
|
|
9
|
+
export default {
|
|
10
|
+
name: '${r}',
|
|
49
11
|
props: {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
default: '',
|
|
53
|
-
},
|
|
54
|
-
style: {
|
|
55
|
-
type: [String, Object],
|
|
56
|
-
default: undefined,
|
|
57
|
-
},
|
|
58
|
-
},
|
|
59
|
-
setup(props: Props) {
|
|
60
|
-
return {};
|
|
12
|
+
className: String,
|
|
13
|
+
style: Object,
|
|
61
14
|
},
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
default: '',
|
|
72
|
-
},
|
|
73
|
-
style: {
|
|
74
|
-
type: [String, Object],
|
|
75
|
-
default: undefined,
|
|
76
|
-
},
|
|
77
|
-
},
|
|
78
|
-
setup(props) {
|
|
79
|
-
return {};
|
|
80
|
-
},
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
export default ${e};
|
|
84
|
-
export { ${e} };`;return `<script${r}>
|
|
85
|
-
import { defineComponent } from 'vue';${n}${s}
|
|
86
|
-
</script>`}
|
|
87
|
-
|
|
88
|
-
export { ee as convertToReact, te as convertToVue, _ as createSvgoConfig, d as formatComponentName, v as optimizeSvg, u as pascalCase, D as readSvgDirectory, K as readSvgFile, I as sanitizeComponentName, k as svgToComponentName, X as writeComponentFile, Q as writeSvgFile };
|
|
15
|
+
};
|
|
16
|
+
`,c+="</script>";let l=`
|
|
17
|
+
<template>
|
|
18
|
+
${e}
|
|
19
|
+
</template>`;return [c,l,`
|
|
20
|
+
<style scoped>
|
|
21
|
+
/* Component styles */
|
|
22
|
+
</style>`].filter(Boolean).join(`
|
|
23
|
+
`)}var b=class extends f{async convert(r,t={}){try{let o=await this.processSvg(r,t),{code:n,componentName:s}=j(o,t),i=this.generateFilename(s,"vue",t.typescript??!0);return {code:n,filename:i,componentName:s}}catch(o){throw new Error(`Failed to convert SVG to Vue: ${o}`)}}};async function Ce(e,r={}){return new b().convert(e,r)}y();export{le as convertToReact,Ce as convertToVue,K as createSvgoConfig,v as formatComponentName,x as optimizeSvg,g as pascalCase,h as readSvgDirectory,O as readSvgFile,X as sanitizeComponentName,Q as svgToComponentName,T as writeComponentFile,D as writeSvgFile};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "svgfusion",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "A powerful CLI tool and library that converts SVG files into production-ready React and Vue 3 components with TypeScript support and automatic optimization.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -13,6 +13,10 @@
|
|
|
13
13
|
"import": "./dist/index.mjs",
|
|
14
14
|
"require": "./dist/index.js"
|
|
15
15
|
},
|
|
16
|
+
"./browser": {
|
|
17
|
+
"import": "./dist/browser.mjs",
|
|
18
|
+
"types": "./dist/browser.d.ts"
|
|
19
|
+
},
|
|
16
20
|
"./cli": {
|
|
17
21
|
"import": "./dist/cli.mjs",
|
|
18
22
|
"require": "./dist/cli.js"
|
|
@@ -48,16 +52,16 @@
|
|
|
48
52
|
"type": "git",
|
|
49
53
|
"url": "git+https://github.com/lolvOid/svgfusion.git"
|
|
50
54
|
},
|
|
51
|
-
"homepage": "https://
|
|
55
|
+
"homepage": "https://svgfusion.netlify.app",
|
|
52
56
|
"bugs": {
|
|
53
57
|
"url": "https://github.com/lolvOid/svgfusion/issues"
|
|
54
58
|
},
|
|
55
59
|
"scripts": {
|
|
56
60
|
"build": "tsup && chmod +x dist/cli.js",
|
|
57
61
|
"dev": "tsup --watch",
|
|
58
|
-
"test": "
|
|
59
|
-
"test:coverage": "
|
|
60
|
-
"test:watch": "
|
|
62
|
+
"test": "jest",
|
|
63
|
+
"test:coverage": "jest --coverage",
|
|
64
|
+
"test:watch": "jest --watch",
|
|
61
65
|
"lint": "eslint src/**/*.ts tests/**/*.ts",
|
|
62
66
|
"lint:fix": "eslint src/**/*.ts tests/**/*.ts --fix",
|
|
63
67
|
"format": "prettier --write src/**/*.ts tests/**/*.ts *.md",
|
|
@@ -65,15 +69,18 @@
|
|
|
65
69
|
"type-check": "tsc --noEmit",
|
|
66
70
|
"clean": "rm -rf dist",
|
|
67
71
|
"prepare": "husky install",
|
|
68
|
-
"prepublishOnly": "
|
|
72
|
+
"prepublishOnly": "pnpm run clean && pnpm run build && pnpm run test",
|
|
69
73
|
"release": "semantic-release",
|
|
70
74
|
"release:dry": "semantic-release --dry-run",
|
|
71
|
-
"
|
|
72
|
-
"
|
|
73
|
-
"
|
|
75
|
+
"dev:docs": "cd docs && pnpm run start",
|
|
76
|
+
"build:docs": "cd docs && pnpm run build",
|
|
77
|
+
"postinstall": "if [ -d docs ]; then cd docs && pnpm install; fi"
|
|
74
78
|
},
|
|
75
79
|
"dependencies": {
|
|
76
80
|
"@svgr/core": "^8.0.0",
|
|
81
|
+
"@svgr/plugin-jsx": "^8.1.0",
|
|
82
|
+
"@svgr/plugin-prettier": "^8.1.0",
|
|
83
|
+
"@svgr/plugin-svgo": "^8.1.0",
|
|
77
84
|
"@types/figlet": "^1.7.0",
|
|
78
85
|
"chalk": "^5.0.0",
|
|
79
86
|
"commander": "^11.0.0",
|
|
@@ -88,18 +95,19 @@
|
|
|
88
95
|
"@semantic-release/git": "^10.0.1",
|
|
89
96
|
"@semantic-release/github": "^11.0.3",
|
|
90
97
|
"@semantic-release/npm": "^12.0.2",
|
|
98
|
+
"@types/jest": "^30.0.0",
|
|
91
99
|
"@types/node": "^20.0.0",
|
|
92
100
|
"@typescript-eslint/eslint-plugin": "^6.21.0",
|
|
93
101
|
"@typescript-eslint/parser": "^6.21.0",
|
|
94
|
-
"@vitest/coverage-v8": "^3.2.4",
|
|
95
102
|
"eslint": "^8.57.0",
|
|
96
103
|
"husky": "^8.0.3",
|
|
104
|
+
"jest": "^30.0.4",
|
|
97
105
|
"lint-staged": "^15.2.2",
|
|
98
106
|
"prettier": "^2.8.8",
|
|
99
107
|
"semantic-release": "^24.2.6",
|
|
100
|
-
"
|
|
101
|
-
"
|
|
102
|
-
"
|
|
108
|
+
"ts-jest": "^29.4.0",
|
|
109
|
+
"tsup": "^8.5.0",
|
|
110
|
+
"typescript": "^5.0.0"
|
|
103
111
|
},
|
|
104
112
|
"engines": {
|
|
105
113
|
"node": ">=18.0.0"
|