svgfusion 1.4.0 → 1.5.1
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 +171 -18
- package/dist/cli.js +215 -20
- package/dist/index.d.mts +10 -2
- package/dist/index.d.ts +10 -2
- package/dist/index.js +3 -6
- package/dist/index.mjs +3 -6
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -28,9 +28,20 @@ A powerful Node.js CLI tool and library that converts SVG files into optimized R
|
|
|
28
28
|
- **Batch Processing**: Convert entire directories of SVG files
|
|
29
29
|
- **Production Ready**: Robust output with proper error handling
|
|
30
30
|
- **Zero Configuration**: Works out of the box with sensible defaults
|
|
31
|
+
- **Simple CLI**: Direct, intuitive command structure without subcommands
|
|
31
32
|
|
|
32
33
|
## Quick Start
|
|
33
34
|
|
|
35
|
+
### Simple as One Command
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
# Convert all SVG files in a directory to React components
|
|
39
|
+
npx svgfusion ./icons --output ./components
|
|
40
|
+
|
|
41
|
+
# Add prefixes, suffixes, and generate index file
|
|
42
|
+
npx svgfusion ./icons --prefix Icon --suffix Component --index
|
|
43
|
+
```
|
|
44
|
+
|
|
34
45
|
### Installation
|
|
35
46
|
|
|
36
47
|
```bash
|
|
@@ -38,12 +49,9 @@ A powerful Node.js CLI tool and library that converts SVG files into optimized R
|
|
|
38
49
|
npm install -g svgfusion
|
|
39
50
|
|
|
40
51
|
# Or use npx (no installation needed)
|
|
41
|
-
npx svgfusion
|
|
52
|
+
npx svgfusion ./icons --output ./components
|
|
42
53
|
|
|
43
54
|
# Or install locally for programmatic usage
|
|
44
|
-
|
|
45
|
-
# Add prefix and suffix to component names
|
|
46
|
-
svgfusion convert ./icons --output ./components --prefix Icon --suffix Svg
|
|
47
55
|
npm install svgfusion
|
|
48
56
|
# or
|
|
49
57
|
yarn add svgfusion
|
|
@@ -53,14 +61,14 @@ pnpm add svgfusion
|
|
|
53
61
|
|
|
54
62
|
## CLI Options
|
|
55
63
|
|
|
56
|
-
<img src="https://i.ibb.co/
|
|
64
|
+
<img src="https://i.ibb.co/TD0QP5FC/cli.png" alt="SVGFusion CLI" width="512" >
|
|
57
65
|
|
|
58
|
-
svgfusion
|
|
66
|
+
svgfusion ./icons --output ./components --prefix Icon --suffix Svg
|
|
59
67
|
|
|
60
68
|
You can add a prefix and/or suffix to the generated component names using the `--prefix` and `--suffix` options:
|
|
61
69
|
|
|
62
70
|
```sh
|
|
63
|
-
npx svgfusion
|
|
71
|
+
npx svgfusion ./svgs --prefix Icon --suffix Svg
|
|
64
72
|
```
|
|
65
73
|
|
|
66
74
|
This will generate components like `IconStarSvg`, `IconUserSvg`, etc.
|
|
@@ -70,7 +78,7 @@ Both options sanitize input to remove symbols and spaces. If omitted, no prefix/
|
|
|
70
78
|
#### Example
|
|
71
79
|
|
|
72
80
|
```sh
|
|
73
|
-
npx svgfusion
|
|
81
|
+
npx svgfusion ./svgs --prefix App --suffix Widget
|
|
74
82
|
# Output: AppStarWidget, AppUserWidget, ...
|
|
75
83
|
```
|
|
76
84
|
|
|
@@ -84,30 +92,40 @@ npx svgfusion --help
|
|
|
84
92
|
|
|
85
93
|
```bash
|
|
86
94
|
# Convert to React components (default)
|
|
87
|
-
svgfusion
|
|
95
|
+
svgfusion ./icons --output ./components
|
|
88
96
|
|
|
89
97
|
# Convert to Vue 3 components
|
|
90
|
-
svgfusion
|
|
98
|
+
svgfusion ./icons --output ./components --framework vue
|
|
91
99
|
|
|
92
100
|
# Single file conversion with TypeScript
|
|
93
|
-
svgfusion
|
|
101
|
+
svgfusion ./star.svg --output ./components --typescript
|
|
102
|
+
|
|
103
|
+
# Batch processing with recursive directory scanning
|
|
104
|
+
svgfusion ./icons --output ./components --recursive
|
|
105
|
+
|
|
106
|
+
# Generate index file for tree-shaking
|
|
107
|
+
svgfusion ./icons --output ./components --index
|
|
94
108
|
|
|
95
109
|
# Skip optimization
|
|
96
|
-
svgfusion
|
|
110
|
+
svgfusion ./icons --output ./components --no-optimize
|
|
97
111
|
|
|
98
112
|
# Using npx (no global install needed)
|
|
99
|
-
npx svgfusion
|
|
113
|
+
npx svgfusion ./icons --output ./components --framework react
|
|
100
114
|
```
|
|
101
115
|
|
|
102
116
|
### CLI Options
|
|
103
117
|
|
|
104
118
|
```bash
|
|
105
|
-
svgfusion
|
|
119
|
+
svgfusion <input> [options]
|
|
106
120
|
|
|
107
121
|
Options:
|
|
108
122
|
-o, --output <output> Output directory (default: "./components")
|
|
109
123
|
-f, --framework <framework> Target framework (react|vue) (default: "react")
|
|
110
124
|
-t, --typescript Generate TypeScript files
|
|
125
|
+
-r, --recursive Recursively scan input directory for SVG files
|
|
126
|
+
--index Generate index file for tree-shaking
|
|
127
|
+
--index-format <format> Index file format (ts|js) (default: "ts")
|
|
128
|
+
--export-type <type> Export type (named|default) (default: "named")
|
|
111
129
|
--no-optimize Skip SVG optimization
|
|
112
130
|
--prefix <prefix> Add prefix to component name (sanitized)
|
|
113
131
|
--suffix <suffix> Add suffix to component name (sanitized)
|
|
@@ -120,17 +138,25 @@ Perfect for trying out SVGFusion or one-time conversions:
|
|
|
120
138
|
|
|
121
139
|
```bash
|
|
122
140
|
# Convert React components
|
|
123
|
-
npx svgfusion
|
|
141
|
+
npx svgfusion ./assets/icons --output ./src/components/icons
|
|
124
142
|
|
|
125
143
|
# Convert Vue components with TypeScript
|
|
126
|
-
npx svgfusion
|
|
144
|
+
npx svgfusion ./assets/icons --output ./src/components --framework vue --typescript
|
|
127
145
|
|
|
128
146
|
# Convert single file
|
|
129
|
-
npx svgfusion
|
|
147
|
+
npx svgfusion ./logo.svg --output ./src/components --framework react
|
|
148
|
+
|
|
149
|
+
# Batch convert with index generation
|
|
150
|
+
npx svgfusion ./assets/icons --output ./src/components --recursive --index
|
|
151
|
+
|
|
152
|
+
# Convert with custom naming
|
|
153
|
+
npx svgfusion ./assets/icons --output ./src/components --prefix Icon --suffix Component --index
|
|
130
154
|
```
|
|
131
155
|
|
|
132
156
|
### Programmatic Usage
|
|
133
157
|
|
|
158
|
+
#### Single File Conversion
|
|
159
|
+
|
|
134
160
|
```typescript
|
|
135
161
|
import { convertToReact, convertToVue, readSvgFile } from 'svgfusion';
|
|
136
162
|
|
|
@@ -156,6 +182,41 @@ console.log(reactResult.code); // Generated React component
|
|
|
156
182
|
console.log(vueResult.code); // Generated Vue component
|
|
157
183
|
```
|
|
158
184
|
|
|
185
|
+
#### Batch Processing
|
|
186
|
+
|
|
187
|
+
```typescript
|
|
188
|
+
import { BatchConverter } from 'svgfusion';
|
|
189
|
+
|
|
190
|
+
const batchConverter = new BatchConverter();
|
|
191
|
+
|
|
192
|
+
// Convert entire directory
|
|
193
|
+
const result = await batchConverter.convertBatch({
|
|
194
|
+
inputDir: './icons',
|
|
195
|
+
outputDir: './components',
|
|
196
|
+
framework: 'react',
|
|
197
|
+
recursive: true,
|
|
198
|
+
generateIndex: true,
|
|
199
|
+
typescript: true,
|
|
200
|
+
prefix: 'Icon',
|
|
201
|
+
suffix: 'Component',
|
|
202
|
+
indexFormat: 'ts',
|
|
203
|
+
exportType: 'named',
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
// Check results
|
|
207
|
+
console.log(`Processed ${result.summary.total} files`);
|
|
208
|
+
console.log(`Successful: ${result.summary.successful}`);
|
|
209
|
+
console.log(`Failed: ${result.summary.failed}`);
|
|
210
|
+
|
|
211
|
+
// Get component names
|
|
212
|
+
const componentNames = batchConverter.getComponentNames(result);
|
|
213
|
+
console.log('Generated components:', componentNames);
|
|
214
|
+
|
|
215
|
+
// Generate summary report
|
|
216
|
+
const report = batchConverter.generateSummaryReport(result);
|
|
217
|
+
console.log(report);
|
|
218
|
+
```
|
|
219
|
+
|
|
159
220
|
## API Reference
|
|
160
221
|
|
|
161
222
|
### `convertToReact(svgContent, options)`
|
|
@@ -186,6 +247,39 @@ Convert SVG to Vue 3 component. **Note: This is a synchronous function.**
|
|
|
186
247
|
- `compositionApi?: boolean` - Use Composition API (default: `true`)
|
|
187
248
|
- `optimize?: boolean` - Apply SVGO optimization (default: `true`)
|
|
188
249
|
|
|
250
|
+
### `BatchConverter`
|
|
251
|
+
|
|
252
|
+
Process multiple SVG files in batch operations.
|
|
253
|
+
|
|
254
|
+
#### `convertBatch(options: BatchConversionOptions)`
|
|
255
|
+
|
|
256
|
+
Convert multiple SVG files to framework components.
|
|
257
|
+
|
|
258
|
+
**Options:**
|
|
259
|
+
|
|
260
|
+
- `inputDir: string` - Input directory path
|
|
261
|
+
- `outputDir: string` - Output directory path
|
|
262
|
+
- `framework?: 'react' | 'vue'` - Target framework (default: 'react')
|
|
263
|
+
- `recursive?: boolean` - Recursively scan directories (default: false)
|
|
264
|
+
- `extensions?: string[]` - File extensions to process (default: ['.svg'])
|
|
265
|
+
- `generateIndex?: boolean` - Generate index file (default: false)
|
|
266
|
+
- `indexFormat?: 'ts' | 'js'` - Index file format (default: 'ts')
|
|
267
|
+
- `exportType?: 'named' | 'default'` - Export type (default: 'named')
|
|
268
|
+
- `prefix?: string` - Add prefix to component names
|
|
269
|
+
- `suffix?: string` - Add suffix to component names
|
|
270
|
+
- `typescript?: boolean` - Generate TypeScript components (default: true)
|
|
271
|
+
- All other conversion options from `convertToReact`/`convertToVue`
|
|
272
|
+
|
|
273
|
+
**Returns:** `Promise<BatchConversionResult>`
|
|
274
|
+
|
|
275
|
+
#### `getComponentNames(results: BatchConversionResult)`
|
|
276
|
+
|
|
277
|
+
Get array of generated component names from batch results.
|
|
278
|
+
|
|
279
|
+
#### `generateSummaryReport(results: BatchConversionResult)`
|
|
280
|
+
|
|
281
|
+
Generate a detailed summary report of the conversion process.
|
|
282
|
+
|
|
189
283
|
### `optimizeSvg(svgContent, config?)`
|
|
190
284
|
|
|
191
285
|
Optimize SVG content using SVGO. **Note: This is a synchronous function.**
|
|
@@ -286,7 +380,7 @@ const customConfig = createSvgoConfig({
|
|
|
286
380
|
const optimizedSvg = optimizeSvg(svgContent, customConfig);
|
|
287
381
|
```
|
|
288
382
|
|
|
289
|
-
### Batch Processing
|
|
383
|
+
### Manual Batch Processing
|
|
290
384
|
|
|
291
385
|
```typescript
|
|
292
386
|
import {
|
|
@@ -308,6 +402,65 @@ for (const svgFile of svgFiles) {
|
|
|
308
402
|
}
|
|
309
403
|
```
|
|
310
404
|
|
|
405
|
+
### Index File Generation
|
|
406
|
+
|
|
407
|
+
When using the `--index` flag or `generateIndex: true` option, SVGFusion creates an optimized index file for tree-shaking:
|
|
408
|
+
|
|
409
|
+
#### Named Exports (Default)
|
|
410
|
+
|
|
411
|
+
```typescript
|
|
412
|
+
// Auto-generated index file for tree-shaking
|
|
413
|
+
// This file exports all components for optimal bundling
|
|
414
|
+
|
|
415
|
+
export { default as IconStar } from './IconStar';
|
|
416
|
+
export { default as IconUser } from './IconUser';
|
|
417
|
+
export { default as IconHome } from './IconHome';
|
|
418
|
+
|
|
419
|
+
// Barrel export for convenience
|
|
420
|
+
export { IconStar, IconUser, IconHome };
|
|
421
|
+
|
|
422
|
+
// TypeScript component types
|
|
423
|
+
export type IconComponent = React.ComponentType<React.SVGProps<SVGSVGElement>>;
|
|
424
|
+
export type IconComponents = {
|
|
425
|
+
IconStar: IconComponent;
|
|
426
|
+
IconUser: IconComponent;
|
|
427
|
+
IconHome: IconComponent;
|
|
428
|
+
};
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
#### Default Exports
|
|
432
|
+
|
|
433
|
+
```typescript
|
|
434
|
+
// Auto-generated index file
|
|
435
|
+
// Warning: Default exports are less tree-shakeable
|
|
436
|
+
|
|
437
|
+
import IconStar from './IconStar';
|
|
438
|
+
import IconUser from './IconUser';
|
|
439
|
+
import IconHome from './IconHome';
|
|
440
|
+
|
|
441
|
+
export default {
|
|
442
|
+
IconStar,
|
|
443
|
+
IconUser,
|
|
444
|
+
IconHome,
|
|
445
|
+
};
|
|
446
|
+
|
|
447
|
+
// Individual exports for flexibility
|
|
448
|
+
export { default as IconStar } from './IconStar';
|
|
449
|
+
export { default as IconUser } from './IconUser';
|
|
450
|
+
export { default as IconHome } from './IconHome';
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
#### Usage
|
|
454
|
+
|
|
455
|
+
```typescript
|
|
456
|
+
// Tree-shakeable named imports (recommended)
|
|
457
|
+
import { IconStar, IconUser } from './components';
|
|
458
|
+
|
|
459
|
+
// Default import
|
|
460
|
+
import * as Icons from './components';
|
|
461
|
+
const { IconStar, IconUser } = Icons;
|
|
462
|
+
```
|
|
463
|
+
|
|
311
464
|
## Complex Filename Support
|
|
312
465
|
|
|
313
466
|
SVGFusion handles complex filenames from design systems:
|
package/dist/cli.js
CHANGED
|
@@ -44,6 +44,7 @@ var init_cjs_shims = __esm({
|
|
|
44
44
|
// src/utils/files.ts
|
|
45
45
|
var files_exports = {};
|
|
46
46
|
__export(files_exports, {
|
|
47
|
+
ensureDir: () => ensureDir,
|
|
47
48
|
ensureDirectoryExists: () => ensureDirectoryExists,
|
|
48
49
|
getComponentFilename: () => getComponentFilename,
|
|
49
50
|
getFileExtension: () => getFileExtension,
|
|
@@ -110,7 +111,7 @@ function getFileExtension(framework, typescript = true) {
|
|
|
110
111
|
function getComponentFilename(_svgFilename, componentName, extension) {
|
|
111
112
|
return `${componentName}${extension}`;
|
|
112
113
|
}
|
|
113
|
-
var import_promises, import_path, import_fs;
|
|
114
|
+
var import_promises, import_path, import_fs, ensureDir;
|
|
114
115
|
var init_files = __esm({
|
|
115
116
|
"src/utils/files.ts"() {
|
|
116
117
|
"use strict";
|
|
@@ -118,6 +119,151 @@ var init_files = __esm({
|
|
|
118
119
|
import_promises = require("fs/promises");
|
|
119
120
|
import_path = require("path");
|
|
120
121
|
import_fs = require("fs");
|
|
122
|
+
ensureDir = ensureDirectoryExists;
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
// src/utils/index-generator.ts
|
|
127
|
+
var index_generator_exports = {};
|
|
128
|
+
__export(index_generator_exports, {
|
|
129
|
+
generateIndexFile: () => generateIndexFile,
|
|
130
|
+
generateReadmeContent: () => generateReadmeContent
|
|
131
|
+
});
|
|
132
|
+
function generateIndexFile(results, options) {
|
|
133
|
+
const { format, exportType, typescript } = options;
|
|
134
|
+
const sortedResults = [...results].sort(
|
|
135
|
+
(a, b) => a.componentName.localeCompare(b.componentName)
|
|
136
|
+
);
|
|
137
|
+
if (exportType === "default") {
|
|
138
|
+
return generateDefaultExports(sortedResults, format, typescript);
|
|
139
|
+
} else {
|
|
140
|
+
return generateNamedExports(sortedResults, format, typescript);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
function generateNamedExports(results, format, typescript) {
|
|
144
|
+
let content = "";
|
|
145
|
+
content += `// Auto-generated index file for tree-shaking
|
|
146
|
+
`;
|
|
147
|
+
content += `// This file exports all components for optimal bundling
|
|
148
|
+
|
|
149
|
+
`;
|
|
150
|
+
for (const result of results) {
|
|
151
|
+
const importPath = getImportPath(result.filename);
|
|
152
|
+
content += `export { default as ${result.componentName} } from './${importPath}';
|
|
153
|
+
`;
|
|
154
|
+
}
|
|
155
|
+
content += `
|
|
156
|
+
// Barrel export for convenience
|
|
157
|
+
`;
|
|
158
|
+
content += `export {
|
|
159
|
+
`;
|
|
160
|
+
for (const result of results) {
|
|
161
|
+
content += ` ${result.componentName},
|
|
162
|
+
`;
|
|
163
|
+
}
|
|
164
|
+
content += `};
|
|
165
|
+
`;
|
|
166
|
+
if (typescript && format === "ts") {
|
|
167
|
+
content += `
|
|
168
|
+
// TypeScript component types
|
|
169
|
+
`;
|
|
170
|
+
content += `export type IconComponent = React.ComponentType<React.SVGProps<SVGSVGElement>>;
|
|
171
|
+
`;
|
|
172
|
+
content += `export type IconComponents = {
|
|
173
|
+
`;
|
|
174
|
+
for (const result of results) {
|
|
175
|
+
content += ` ${result.componentName}: IconComponent;
|
|
176
|
+
`;
|
|
177
|
+
}
|
|
178
|
+
content += `};
|
|
179
|
+
`;
|
|
180
|
+
}
|
|
181
|
+
return content;
|
|
182
|
+
}
|
|
183
|
+
function generateDefaultExports(results, _format, _typescript) {
|
|
184
|
+
let content = "";
|
|
185
|
+
content += `// Auto-generated index file
|
|
186
|
+
`;
|
|
187
|
+
content += `// Warning: Default exports are less tree-shakeable
|
|
188
|
+
|
|
189
|
+
`;
|
|
190
|
+
for (const result of results) {
|
|
191
|
+
const importPath = getImportPath(result.filename);
|
|
192
|
+
content += `import ${result.componentName} from './${importPath}';
|
|
193
|
+
`;
|
|
194
|
+
}
|
|
195
|
+
content += `
|
|
196
|
+
export default {
|
|
197
|
+
`;
|
|
198
|
+
for (const result of results) {
|
|
199
|
+
content += ` ${result.componentName},
|
|
200
|
+
`;
|
|
201
|
+
}
|
|
202
|
+
content += `};
|
|
203
|
+
`;
|
|
204
|
+
content += `
|
|
205
|
+
// Individual exports for flexibility
|
|
206
|
+
`;
|
|
207
|
+
for (const result of results) {
|
|
208
|
+
content += `export { default as ${result.componentName} } from './${getImportPath(result.filename)}';
|
|
209
|
+
`;
|
|
210
|
+
}
|
|
211
|
+
return content;
|
|
212
|
+
}
|
|
213
|
+
function getImportPath(filename) {
|
|
214
|
+
return filename.replace(/\.(tsx?|jsx?|vue)$/, "");
|
|
215
|
+
}
|
|
216
|
+
function generateReadmeContent(results) {
|
|
217
|
+
const componentNames = results.map((r) => r.componentName).sort();
|
|
218
|
+
let content = `# Generated Components
|
|
219
|
+
|
|
220
|
+
`;
|
|
221
|
+
content += `This directory contains ${results.length} auto-generated components from SVG files.
|
|
222
|
+
|
|
223
|
+
`;
|
|
224
|
+
content += `## Usage
|
|
225
|
+
|
|
226
|
+
`;
|
|
227
|
+
content += `### Named imports (recommended for tree-shaking)
|
|
228
|
+
|
|
229
|
+
`;
|
|
230
|
+
content += `\`\`\`typescript
|
|
231
|
+
`;
|
|
232
|
+
content += `import { ${componentNames.slice(0, 3).join(", ")} } from './index';
|
|
233
|
+
`;
|
|
234
|
+
content += `\`\`\`
|
|
235
|
+
|
|
236
|
+
`;
|
|
237
|
+
content += `### Default import
|
|
238
|
+
|
|
239
|
+
`;
|
|
240
|
+
content += `\`\`\`typescript
|
|
241
|
+
`;
|
|
242
|
+
content += `import * as Icons from './index';
|
|
243
|
+
`;
|
|
244
|
+
content += `\`\`\`
|
|
245
|
+
|
|
246
|
+
`;
|
|
247
|
+
content += `## Available Components
|
|
248
|
+
|
|
249
|
+
`;
|
|
250
|
+
for (const name of componentNames) {
|
|
251
|
+
content += `- \`${name}\`
|
|
252
|
+
`;
|
|
253
|
+
}
|
|
254
|
+
content += `
|
|
255
|
+
## Tree-shaking
|
|
256
|
+
|
|
257
|
+
`;
|
|
258
|
+
content += `This index file is optimized for tree-shaking. When using named imports, `;
|
|
259
|
+
content += `bundlers like webpack, Rollup, or Vite will only include the components you actually use.
|
|
260
|
+
`;
|
|
261
|
+
return content;
|
|
262
|
+
}
|
|
263
|
+
var init_index_generator = __esm({
|
|
264
|
+
"src/utils/index-generator.ts"() {
|
|
265
|
+
"use strict";
|
|
266
|
+
init_cjs_shims();
|
|
121
267
|
}
|
|
122
268
|
});
|
|
123
269
|
|
|
@@ -130,11 +276,20 @@ var import_path2 = require("path");
|
|
|
130
276
|
|
|
131
277
|
// src/utils/name.ts
|
|
132
278
|
init_cjs_shims();
|
|
133
|
-
|
|
279
|
+
|
|
280
|
+
// src/utils/string.ts
|
|
281
|
+
init_cjs_shims();
|
|
282
|
+
var pascalCase = (str) => {
|
|
283
|
+
return str.match(
|
|
284
|
+
/[A-Z]{2,}(?=[A-Z][a-z]+[0-9]*|\b)|[A-Z]?[a-z]+[0-9]*[a-z]*|[A-Z]|[0-9]+[a-z]*/g
|
|
285
|
+
)?.map((x) => x.charAt(0).toUpperCase() + x.slice(1).toLowerCase()).join("") || "";
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
// src/utils/name.ts
|
|
134
289
|
function svgToComponentName(filename) {
|
|
135
290
|
let baseName = filename.replace(/\.svg$/i, "");
|
|
136
291
|
baseName = processComplexFilename(baseName);
|
|
137
|
-
return (
|
|
292
|
+
return pascalCase(baseName);
|
|
138
293
|
}
|
|
139
294
|
function processComplexFilename(filename) {
|
|
140
295
|
let processed = filename.toLowerCase().replace(/[^a-zA-Z0-9]/g, " ");
|
|
@@ -142,9 +297,9 @@ function processComplexFilename(filename) {
|
|
|
142
297
|
return processed;
|
|
143
298
|
}
|
|
144
299
|
function formatComponentName(name, prefix, suffix) {
|
|
145
|
-
const prefixPart = prefix ? (
|
|
146
|
-
const suffixPart = suffix ? (
|
|
147
|
-
const baseName = (
|
|
300
|
+
const prefixPart = prefix ? pascalCase(prefix) : "";
|
|
301
|
+
const suffixPart = suffix ? pascalCase(suffix) : "";
|
|
302
|
+
const baseName = pascalCase(name);
|
|
148
303
|
return `${prefixPart}${baseName}${suffixPart}`;
|
|
149
304
|
}
|
|
150
305
|
|
|
@@ -477,11 +632,7 @@ export default {
|
|
|
477
632
|
<template>
|
|
478
633
|
${svg}
|
|
479
634
|
</template>`;
|
|
480
|
-
|
|
481
|
-
<style scoped>
|
|
482
|
-
/* Component styles */
|
|
483
|
-
</style>`;
|
|
484
|
-
return [script, template, style].filter(Boolean).join("\n");
|
|
635
|
+
return [script, template].filter(Boolean).join("\n");
|
|
485
636
|
}
|
|
486
637
|
|
|
487
638
|
// src/core/vue-converter.ts
|
|
@@ -624,16 +775,35 @@ if (process.argv.length === 1 || isNpxCall && process.argv.length === 2) {
|
|
|
624
775
|
program.name("svgfusion").description(
|
|
625
776
|
"Transform SVG files into production-ready React and Vue 3 components"
|
|
626
777
|
).version(packageJson.version);
|
|
627
|
-
program.
|
|
778
|
+
program.argument("[input]", "Input SVG file or directory").option("-o, --output <output>", "Output directory", "./components").option(
|
|
628
779
|
"-f, --framework <framework>",
|
|
629
780
|
"Target framework (react|vue)",
|
|
630
781
|
"react"
|
|
631
|
-
).option("-t, --typescript", "Generate TypeScript files", false).option(
|
|
782
|
+
).option("-t, --typescript", "Generate TypeScript files", false).option(
|
|
783
|
+
"-r, --recursive",
|
|
784
|
+
"Recursively scan input directory for SVG files",
|
|
785
|
+
false
|
|
786
|
+
).option("--no-optimize", "Skip SVG optimization").option("--prefix <prefix>", "Add prefix to component name").option("--suffix <suffix>", "Add suffix to component name").option("--index", "Generate index file for tree-shaking", false).option("--index-format <format>", "Index file format (ts|js)", "ts").option("--export-type <type>", "Export type (named|default)", "named").action(
|
|
632
787
|
async (input, options) => {
|
|
788
|
+
if (!input) {
|
|
789
|
+
program.outputHelp();
|
|
790
|
+
process.exit(0);
|
|
791
|
+
}
|
|
633
792
|
console.log(createBanner(colors));
|
|
634
793
|
console.log(`${colors.blue}\u{1F504} Processing SVG files...${colors.reset}`);
|
|
635
794
|
try {
|
|
636
|
-
const {
|
|
795
|
+
const {
|
|
796
|
+
framework,
|
|
797
|
+
output,
|
|
798
|
+
typescript,
|
|
799
|
+
recursive,
|
|
800
|
+
optimize: optimize2,
|
|
801
|
+
prefix,
|
|
802
|
+
suffix,
|
|
803
|
+
index: generateIndex,
|
|
804
|
+
indexFormat,
|
|
805
|
+
exportType
|
|
806
|
+
} = options;
|
|
637
807
|
if (framework !== "react" && framework !== "vue") {
|
|
638
808
|
throw new Error('Framework must be either "react" or "vue"');
|
|
639
809
|
}
|
|
@@ -646,7 +816,7 @@ program.description("Convert SVG files to React or Vue components").argument("<i
|
|
|
646
816
|
throw new Error("Input file must be an SVG file");
|
|
647
817
|
}
|
|
648
818
|
} else if (inputStat.isDirectory()) {
|
|
649
|
-
svgFiles = await readSvgDirectory(input);
|
|
819
|
+
svgFiles = await readSvgDirectory(input, recursive);
|
|
650
820
|
} else {
|
|
651
821
|
throw new Error("Input must be a file or directory");
|
|
652
822
|
}
|
|
@@ -656,6 +826,7 @@ program.description("Convert SVG files to React or Vue components").argument("<i
|
|
|
656
826
|
console.log(
|
|
657
827
|
`${colors.blue}\u{1F504} Converting ${svgFiles.length} SVG file(s)...${colors.reset}`
|
|
658
828
|
);
|
|
829
|
+
const results = [];
|
|
659
830
|
for (const filePath of svgFiles) {
|
|
660
831
|
const svgContent = await readSvgFile(filePath);
|
|
661
832
|
const optimizedSvg = optimize2 ? optimizeSvg(svgContent) : svgContent;
|
|
@@ -674,6 +845,21 @@ program.description("Convert SVG files to React or Vue components").argument("<i
|
|
|
674
845
|
});
|
|
675
846
|
const outputPath = (0, import_path3.join)(output, result.filename);
|
|
676
847
|
await writeComponentFile(outputPath, result.code);
|
|
848
|
+
results.push(result);
|
|
849
|
+
}
|
|
850
|
+
if (generateIndex && results.length > 0) {
|
|
851
|
+
const { generateIndexFile: generateIndexFile2 } = await Promise.resolve().then(() => (init_index_generator(), index_generator_exports));
|
|
852
|
+
const indexContent = generateIndexFile2(results, {
|
|
853
|
+
format: indexFormat,
|
|
854
|
+
exportType,
|
|
855
|
+
typescript
|
|
856
|
+
});
|
|
857
|
+
const indexFilename = `index.${indexFormat}`;
|
|
858
|
+
const indexPath = (0, import_path3.join)(output, indexFilename);
|
|
859
|
+
await writeComponentFile(indexPath, indexContent);
|
|
860
|
+
console.log(
|
|
861
|
+
`${colors.green}\u{1F4C4} Generated ${indexFilename} for tree-shaking${colors.reset}`
|
|
862
|
+
);
|
|
677
863
|
}
|
|
678
864
|
console.log(
|
|
679
865
|
`${colors.green}\u2705 Successfully converted ${svgFiles.length} SVG file(s) to ${framework} components${colors.reset}`
|
|
@@ -681,6 +867,10 @@ program.description("Convert SVG files to React or Vue components").argument("<i
|
|
|
681
867
|
console.log(
|
|
682
868
|
`${colors.dim}\u{1F4C1} Output location: ${colors.reset}${colors.cyan}${output}${colors.reset}`
|
|
683
869
|
);
|
|
870
|
+
const componentNames = results.map((r) => r.componentName);
|
|
871
|
+
console.log(
|
|
872
|
+
`${colors.dim}\u{1F4E6} Generated components: ${colors.reset}${componentNames.join(", ")}`
|
|
873
|
+
);
|
|
684
874
|
} catch (error) {
|
|
685
875
|
console.error(
|
|
686
876
|
`${colors.red}\u274C Error: ${error instanceof Error ? error.message : "Unknown error"}${colors.reset}`
|
|
@@ -692,10 +882,15 @@ program.description("Convert SVG files to React or Vue components").argument("<i
|
|
|
692
882
|
"after",
|
|
693
883
|
`
|
|
694
884
|
${colors.gray}Examples:${colors.reset}
|
|
695
|
-
${colors.blue}svgfusion
|
|
696
|
-
${colors.blue}svgfusion
|
|
697
|
-
${colors.blue}svgfusion
|
|
698
|
-
${colors.blue}svgfusion
|
|
885
|
+
${colors.blue}svgfusion src/icons -o src/components${colors.reset}
|
|
886
|
+
${colors.blue}svgfusion src/icons --framework vue --typescript${colors.reset}
|
|
887
|
+
${colors.blue}svgfusion src/icons --recursive --index${colors.reset}
|
|
888
|
+
${colors.blue}svgfusion src/icons --prefix Icon --suffix Component --index${colors.reset}
|
|
889
|
+
${colors.blue}svgfusion src/icons --framework react --typescript --optimize${colors.reset}
|
|
699
890
|
`
|
|
700
|
-
)
|
|
891
|
+
);
|
|
892
|
+
if (process.argv.length === 2) {
|
|
893
|
+
program.outputHelp();
|
|
894
|
+
process.exit(0);
|
|
895
|
+
}
|
|
701
896
|
program.parse();
|
package/dist/index.d.mts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { Config } from 'svgo';
|
|
2
|
-
export { default as pascalCase } from 'just-pascal-case';
|
|
3
2
|
|
|
4
3
|
interface ConversionOptions {
|
|
5
4
|
name?: string;
|
|
@@ -41,6 +40,10 @@ interface BatchConversionOptions extends ConversionOptions {
|
|
|
41
40
|
outputDir: string;
|
|
42
41
|
recursive?: boolean;
|
|
43
42
|
extensions?: string[];
|
|
43
|
+
generateIndex?: boolean;
|
|
44
|
+
indexFormat?: 'ts' | 'js';
|
|
45
|
+
exportType?: 'named' | 'default';
|
|
46
|
+
framework?: Framework;
|
|
44
47
|
}
|
|
45
48
|
interface BatchConversionResult {
|
|
46
49
|
results: ConversionResult[];
|
|
@@ -69,6 +72,9 @@ interface CliOptions {
|
|
|
69
72
|
format?: 'esm' | 'cjs';
|
|
70
73
|
recursive?: boolean;
|
|
71
74
|
verbose?: boolean;
|
|
75
|
+
generateIndex?: boolean;
|
|
76
|
+
indexFormat?: 'ts' | 'js';
|
|
77
|
+
exportType?: 'named' | 'default';
|
|
72
78
|
}
|
|
73
79
|
|
|
74
80
|
/**
|
|
@@ -129,6 +135,8 @@ declare function writeComponentFile(filePath: string, content: string): Promise<
|
|
|
129
135
|
*/
|
|
130
136
|
declare function readSvgDirectory(dirPath: string, recursive?: boolean): Promise<string[]>;
|
|
131
137
|
|
|
138
|
+
declare const pascalCase: (str: string) => string;
|
|
139
|
+
|
|
132
140
|
/**
|
|
133
141
|
* Convert SVG filename to a valid React component name
|
|
134
142
|
* @param filename - The SVG filename
|
|
@@ -151,4 +159,4 @@ declare function sanitizeComponentName(name: string): string;
|
|
|
151
159
|
*/
|
|
152
160
|
declare function formatComponentName(name: string, prefix?: string, suffix?: string): string;
|
|
153
161
|
|
|
154
|
-
export { type BatchConversionOptions, type BatchConversionResult, type CliOptions, type ConversionError, type ConversionOptions, type ConversionResult, type Framework, type ReactConversionOptions, type VueConversionOptions, convertToReact, convertToVue, createSvgoConfig, formatComponentName, optimizeSvg, readSvgDirectory, readSvgFile, sanitizeComponentName, svgToComponentName, writeComponentFile, writeSvgFile };
|
|
162
|
+
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
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { Config } from 'svgo';
|
|
2
|
-
export { default as pascalCase } from 'just-pascal-case';
|
|
3
2
|
|
|
4
3
|
interface ConversionOptions {
|
|
5
4
|
name?: string;
|
|
@@ -41,6 +40,10 @@ interface BatchConversionOptions extends ConversionOptions {
|
|
|
41
40
|
outputDir: string;
|
|
42
41
|
recursive?: boolean;
|
|
43
42
|
extensions?: string[];
|
|
43
|
+
generateIndex?: boolean;
|
|
44
|
+
indexFormat?: 'ts' | 'js';
|
|
45
|
+
exportType?: 'named' | 'default';
|
|
46
|
+
framework?: Framework;
|
|
44
47
|
}
|
|
45
48
|
interface BatchConversionResult {
|
|
46
49
|
results: ConversionResult[];
|
|
@@ -69,6 +72,9 @@ interface CliOptions {
|
|
|
69
72
|
format?: 'esm' | 'cjs';
|
|
70
73
|
recursive?: boolean;
|
|
71
74
|
verbose?: boolean;
|
|
75
|
+
generateIndex?: boolean;
|
|
76
|
+
indexFormat?: 'ts' | 'js';
|
|
77
|
+
exportType?: 'named' | 'default';
|
|
72
78
|
}
|
|
73
79
|
|
|
74
80
|
/**
|
|
@@ -129,6 +135,8 @@ declare function writeComponentFile(filePath: string, content: string): Promise<
|
|
|
129
135
|
*/
|
|
130
136
|
declare function readSvgDirectory(dirPath: string, recursive?: boolean): Promise<string[]>;
|
|
131
137
|
|
|
138
|
+
declare const pascalCase: (str: string) => string;
|
|
139
|
+
|
|
132
140
|
/**
|
|
133
141
|
* Convert SVG filename to a valid React component name
|
|
134
142
|
* @param filename - The SVG filename
|
|
@@ -151,4 +159,4 @@ declare function sanitizeComponentName(name: string): string;
|
|
|
151
159
|
*/
|
|
152
160
|
declare function formatComponentName(name: string, prefix?: string, suffix?: string): string;
|
|
153
161
|
|
|
154
|
-
export { type BatchConversionOptions, type BatchConversionResult, type CliOptions, type ConversionError, type ConversionOptions, type ConversionResult, type Framework, type ReactConversionOptions, type VueConversionOptions, convertToReact, convertToVue, createSvgoConfig, formatComponentName, optimizeSvg, readSvgDirectory, readSvgFile, sanitizeComponentName, svgToComponentName, writeComponentFile, writeSvgFile };
|
|
162
|
+
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,4 +1,4 @@
|
|
|
1
|
-
'use strict';var promises=require('fs/promises'),path=require('path'),fs=require('fs'),core=require('@svgr/core'),svgo=require('svgo')
|
|
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 G=Object.getOwnPropertyDescriptor;var I=Object.getOwnPropertyNames;var k=Object.prototype.hasOwnProperty;var R=(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});},Z=(e,r,t,o)=>{if(r&&typeof r=="object"||typeof r=="function")for(let n of I(r))!k.call(e,n)&&n!==t&&d(e,n,{get:()=>r[n],enumerable:!(o=G(r,n))||o.enumerable});return e};var _=e=>Z(d({},"__esModule",{value:true}),e);var i=R(()=>{});var D={};q(D,{ensureDir:()=>se,ensureDirectoryExists:()=>v,getComponentFilename:()=>ae,getFileExtension:()=>ie,readSvgDirectory:()=>x,readSvgFile:()=>O,writeComponentFile:()=>A,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 v(path.dirname(e)),await promises.writeFile(e,r,"utf-8");}catch(t){throw new Error(`Failed to write SVG file: ${e}. ${t}`)}}async function A(e,r){try{await v(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),a=await promises.stat(s);if(a.isDirectory()&&r){let m=await x(s,r);o.push(...m);}else a.isFile()&&path.extname(n).toLowerCase()===".svg"&&o.push(s);}return o}catch(t){throw new Error(`Failed to read directory: ${e}. ${t}`)}}async function v(e){fs.existsSync(e)||await promises.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 se,h=R(()=>{i();se=v;});i();i();i();i();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}}i();i();var u=e=>e.match(/[A-Z]{2,}(?=[A-Z][a-z]+[0-9]*|\b)|[A-Z]?[a-z]+[0-9]*[a-z]*|[A-Z]|[0-9]+[a-z]*/g)?.map(r=>r.charAt(0).toUpperCase()+r.slice(1).toLowerCase()).join("")||"";function K(e){let r=e.replace(/\.svg$/i,"");return r=Q(r),u(r)}function Q(e){let r=e.toLowerCase().replace(/[^a-zA-Z0-9]/g," ");return r=r.replace(/\s+/g," ").trim(),r}function W(e){return u(e.replace(/[^a-zA-Z0-9]/g," "))}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}=(h(),_(D)),a=n(t,o);return s("icon.svg",r,a)}catch{return `${r}${{react:o?".tsx":".jsx",vue:".vue"}[t]}`}}};i();function z(e){let{typescript:r=true,memo:t=true,ref:o=true,titleProp:n=true,descProp:s=true,icon:a=true,dimensions:m=false,replaceAttrValues:c={"#000":"currentColor","#000000":"currentColor"},svgProps:l={},expandProps:S=false,nativeProps:T=true,ariaLabelledBy:B=false,ariaHidden:L=false,role:j="img"}=e,b={typescript:r,memo:t,ref:o,titleProp:n,descProp:s,icon:a,dimensions:m,expandProps:S,svgProps:{className:"{className}",...o&&{ref:"{ref}"},...T&&{width:"{width}",height:"{height}",style:"{style}"},...B&&n&&s&&{"aria-labelledby":"{titleId} {descId}"},...L&&{"aria-hidden":"true"},role:j,...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}}},...a&&!m?[{name:"removeAttrs",params:{attrs:["width","height"]}}]:[],...a?["cleanupNumericValues"]:[]]},b}function U(e,r,t){let o=e;return w(o)&&(o=$(o,r)),o}function w(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 y=class extends g{async convert(r,t={}){try{let o=this.generateComponentName(t),n=await this.processSvg(r,t);w(n)&&(n=$(n,o));let s=z(t),a=await core.transform(n,s,{componentName:o}),m=U(a,o,t),c=this.generateFilename(o,"react",t.typescript??!0);return {code:m,filename:c,componentName:o}}catch(o){throw new Error(`Failed to convert SVG to React: ${o}`)}}};async function le(e,r={}){return new y().convert(e,r)}i();i();function E(e,r){let{name:t,prefix:o,suffix:n,props:s=true,replaceAttrValues:a={"#000":"currentColor","#000000":"currentColor"}}=r,c=f(t||"Icon",o,n),l=pe(e);return l=me(l,a),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+=`
|
|
2
2
|
interface Props {
|
|
3
3
|
className?: string;
|
|
4
4
|
style?: Record<string, any>;
|
|
@@ -16,8 +16,5 @@ export default {
|
|
|
16
16
|
`,c+="</script>";let l=`
|
|
17
17
|
<template>
|
|
18
18
|
${e}
|
|
19
|
-
</template>`;return [c,l
|
|
20
|
-
|
|
21
|
-
/* Component styles */
|
|
22
|
-
</style>`].filter(Boolean).join(`
|
|
23
|
-
`)}var S=class extends g{async convert(r,t={}){try{let o=await this.processSvg(r,t),{code:n,componentName:s}=T(o,t),a=this.generateFilename(s,"vue",t.typescript??!0);return {code:n,filename:a,componentName:s}}catch(o){throw new Error(`Failed to convert SVG to Vue: ${o}`)}}};async function ve(e,r={}){return new S().convert(e,r)}w();Object.defineProperty(exports,"pascalCase",{enumerable:true,get:function(){return u__default.default}});exports.convertToReact=ce;exports.convertToVue=ve;exports.createSvgoConfig=J;exports.formatComponentName=f;exports.optimizeSvg=C;exports.readSvgDirectory=x;exports.readSvgFile=O;exports.sanitizeComponentName=W;exports.svgToComponentName=K;exports.writeComponentFile=D;exports.writeSvgFile=P;
|
|
19
|
+
</template>`;return [c,l].filter(Boolean).join(`
|
|
20
|
+
`)}var F=class extends g{async convert(r,t={}){try{let o=await this.processSvg(r,t),{code:n,componentName:s}=E(o,t),a=this.generateFilename(s,"vue",t.typescript??!0);return {code:n,filename:a,componentName:s}}catch(o){throw new Error(`Failed to convert SVG to Vue: ${o}`)}}};async function de(e,r={}){return new F().convert(e,r)}h();exports.convertToReact=le;exports.convertToVue=de;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=A;exports.writeSvgFile=P;
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
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';
|
|
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 _=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});},Z=(e,r,t,o)=>{if(r&&typeof r=="object"||typeof r=="function")for(let n of _(r))!I.call(e,n)&&n!==t&&d(e,n,{get:()=>r[n],enumerable:!(o=k(r,n))||o.enumerable});return e};var H=e=>Z(d({},"__esModule",{value:true}),e);var i=N(()=>{});var z={};q(z,{ensureDir:()=>ie,ensureDirectoryExists:()=>C,getComponentFilename:()=>ce,getFileExtension:()=>ae,readSvgDirectory:()=>h,readSvgFile:()=>O,writeComponentFile:()=>D,writeSvgFile:()=>A});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 A(e,r){try{await C(dirname(e)),await writeFile(e,r,"utf-8");}catch(t){throw new Error(`Failed to write SVG file: ${e}. ${t}`)}}async function D(e,r){try{await C(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),a=await stat(s);if(a.isDirectory()&&r){let u=await h(s,r);o.push(...u);}else a.isFile()&&extname(n).toLowerCase()===".svg"&&o.push(s);}return o}catch(t){throw new Error(`Failed to read directory: ${e}. ${t}`)}}async function C(e){existsSync(e)||await mkdir(e,{recursive:true});}function ae(e,r=true){return e==="react"?r?".tsx":".jsx":".vue"}function ce(e,r,t){return `${r}${t}`}var ie,w=N(()=>{i();ie=C;});i();i();i();i();var K={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=K){try{return optimize(e,r).data}catch(t){throw new Error(`Failed to optimize SVG: ${t}`)}}function M(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}}i();i();var g=e=>e.match(/[A-Z]{2,}(?=[A-Z][a-z]+[0-9]*|\b)|[A-Z]?[a-z]+[0-9]*[a-z]*|[A-Z]|[0-9]+[a-z]*/g)?.map(r=>r.charAt(0).toUpperCase()+r.slice(1).toLowerCase()).join("")||"";function Q(e){let r=e.replace(/\.svg$/i,"");return r=W(r),g(r)}function W(e){let r=e.toLowerCase().replace(/[^a-zA-Z0-9]/g," ");return r=r.replace(/\s+/g," ").trim(),r}function X(e){return g(e.replace(/[^a-zA-Z0-9]/g," "))}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}=(w(),H(z)),a=n(t,o);return s("icon.svg",r,a)}catch{return `${r}${{react:o?".tsx":".jsx",vue:".vue"}[t]}`}}};i();function T(e){let{typescript:r=true,memo:t=true,ref:o=true,titleProp:n=true,descProp:s=true,icon:a=true,dimensions:u=false,replaceAttrValues:c={"#000":"currentColor","#000000":"currentColor"},svgProps:l={},expandProps:b=false,nativeProps:U=true,ariaLabelledBy:G=false,ariaHidden:L=false,role:j="img"}=e,V={typescript:r,memo:t,ref:o,titleProp:n,descProp:s,icon:a,dimensions:u,expandProps:b,svgProps:{className:"{className}",...o&&{ref:"{ref}"},...U&&{width:"{width}",height:"{height}",style:"{style}"},...G&&n&&s&&{"aria-labelledby":"{titleId} {descId}"},...L&&{"aria-hidden":"true"},role:j,...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}}},...a&&!u?[{name:"removeAttrs",params:{attrs:["width","height"]}}]:[],...a?["cleanupNumericValues"]:[]]},V}function E(e,r,t){let o=e;return $(o)&&(o=y(o,r)),o}function $(e){return ["linearGradient","radialGradient","pattern","mask","filter","clipPath","marker","symbol","use"].some(t=>e.includes(`<${t}`)||e.includes(`</${t}`))}function y(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 F=class extends f{async convert(r,t={}){try{let o=this.generateComponentName(t),n=await this.processSvg(r,t);$(n)&&(n=y(n,o));let s=T(t),a=await transform(n,s,{componentName:o}),u=E(a,o,t),c=this.generateFilename(o,"react",t.typescript??!0);return {code:u,filename:c,componentName:o}}catch(o){throw new Error(`Failed to convert SVG to React: ${o}`)}}};async function pe(e,r={}){return new F().convert(e,r)}i();i();function B(e,r){let{name:t,prefix:o,suffix:n,props:s=true,replaceAttrValues:a={"#000":"currentColor","#000000":"currentColor"}}=r,c=v(t||"Icon",o,n),l=me(e);return l=ue(l,a),ge(l)&&(l=fe(l,c)),l=ve(l,s),{code:Ce(l,c,r),componentName:c}}function me(e){return e.replace(/<\?xml[^>]*\?>\s*/,"").replace(/<!--[\s\S]*?-->/g,"").replace(/xmlns="[^"]*"/g,"").trim()}function ue(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 ge(e){return ["linearGradient","radialGradient","pattern","mask","filter","clipPath","marker","symbol","use"].some(t=>e.includes(`<${t}`)||e.includes(`</${t}`))}function fe(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 ve(e,r){return r?e.replace("<svg",'<svg :class="className" :style="style" v-bind="$attrs"'):e}function Ce(e,r,t){let{typescript:o,compositionApi:n,props:s}=t,c=`<script${o?' lang="ts"':""}${n?" setup":""}>`;n?s&&(c+=`
|
|
2
2
|
interface Props {
|
|
3
3
|
className?: string;
|
|
4
4
|
style?: Record<string, any>;
|
|
@@ -16,8 +16,5 @@ export default {
|
|
|
16
16
|
`,c+="</script>";let l=`
|
|
17
17
|
<template>
|
|
18
18
|
${e}
|
|
19
|
-
</template>`;return [c,l
|
|
20
|
-
|
|
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}=B(o,t),a=this.generateFilename(s,"vue",t.typescript??!0);return {code:n,filename:a,componentName:s}}catch(o){throw new Error(`Failed to convert SVG to Vue: ${o}`)}}};async function de(e,r={}){return new b().convert(e,r)}y();export{le as convertToReact,de as convertToVue,M as createSvgoConfig,v as formatComponentName,x as optimizeSvg,h as readSvgDirectory,O as readSvgFile,X as sanitizeComponentName,Q as svgToComponentName,T as writeComponentFile,D as writeSvgFile};
|
|
19
|
+
</template>`;return [c,l].filter(Boolean).join(`
|
|
20
|
+
`)}var S=class extends f{async convert(r,t={}){try{let o=await this.processSvg(r,t),{code:n,componentName:s}=B(o,t),a=this.generateFilename(s,"vue",t.typescript??!0);return {code:n,filename:a,componentName:s}}catch(o){throw new Error(`Failed to convert SVG to Vue: ${o}`)}}};async function de(e,r={}){return new S().convert(e,r)}w();export{pe as convertToReact,de as convertToVue,M as createSvgoConfig,v as formatComponentName,x as optimizeSvg,g as pascalCase,h as readSvgDirectory,O as readSvgFile,X as sanitizeComponentName,Q as svgToComponentName,D as writeComponentFile,A as writeSvgFile};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "svgfusion",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.1",
|
|
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",
|
|
@@ -90,13 +90,13 @@
|
|
|
90
90
|
"chalk": "^5.0.0",
|
|
91
91
|
"commander": "^11.0.0",
|
|
92
92
|
"figlet": "^1.8.1",
|
|
93
|
-
"just-pascal-case": "^3.2.0",
|
|
94
93
|
"ora": "^7.0.0",
|
|
95
94
|
"svgo": "^3.0.0"
|
|
96
95
|
},
|
|
97
96
|
"devDependencies": {
|
|
98
97
|
"@commitlint/cli": "^17.8.1",
|
|
99
98
|
"@commitlint/config-conventional": "^17.8.1",
|
|
99
|
+
"@jest/globals": "^30.0.4",
|
|
100
100
|
"@semantic-release/changelog": "^6.0.3",
|
|
101
101
|
"@semantic-release/git": "^10.0.1",
|
|
102
102
|
"@semantic-release/github": "^11.0.3",
|