svgfusion 1.3.0 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +172 -17
- package/dist/cli.js +279 -67
- 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,12 +61,14 @@ pnpm add svgfusion
|
|
|
53
61
|
|
|
54
62
|
## CLI Options
|
|
55
63
|
|
|
56
|
-
|
|
64
|
+
<img src="https://i.ibb.co/8n2b5mtp/cli.png" alt="SVGFusion CLI" width="512" >
|
|
65
|
+
|
|
66
|
+
svgfusion ./icons --output ./components --prefix Icon --suffix Svg
|
|
57
67
|
|
|
58
68
|
You can add a prefix and/or suffix to the generated component names using the `--prefix` and `--suffix` options:
|
|
59
69
|
|
|
60
70
|
```sh
|
|
61
|
-
npx svgfusion
|
|
71
|
+
npx svgfusion ./svgs --prefix Icon --suffix Svg
|
|
62
72
|
```
|
|
63
73
|
|
|
64
74
|
This will generate components like `IconStarSvg`, `IconUserSvg`, etc.
|
|
@@ -68,7 +78,7 @@ Both options sanitize input to remove symbols and spaces. If omitted, no prefix/
|
|
|
68
78
|
#### Example
|
|
69
79
|
|
|
70
80
|
```sh
|
|
71
|
-
npx svgfusion
|
|
81
|
+
npx svgfusion ./svgs --prefix App --suffix Widget
|
|
72
82
|
# Output: AppStarWidget, AppUserWidget, ...
|
|
73
83
|
```
|
|
74
84
|
|
|
@@ -82,30 +92,40 @@ npx svgfusion --help
|
|
|
82
92
|
|
|
83
93
|
```bash
|
|
84
94
|
# Convert to React components (default)
|
|
85
|
-
svgfusion
|
|
95
|
+
svgfusion ./icons --output ./components
|
|
86
96
|
|
|
87
97
|
# Convert to Vue 3 components
|
|
88
|
-
svgfusion
|
|
98
|
+
svgfusion ./icons --output ./components --framework vue
|
|
89
99
|
|
|
90
100
|
# Single file conversion with TypeScript
|
|
91
|
-
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
|
|
92
108
|
|
|
93
109
|
# Skip optimization
|
|
94
|
-
svgfusion
|
|
110
|
+
svgfusion ./icons --output ./components --no-optimize
|
|
95
111
|
|
|
96
112
|
# Using npx (no global install needed)
|
|
97
|
-
npx svgfusion
|
|
113
|
+
npx svgfusion ./icons --output ./components --framework react
|
|
98
114
|
```
|
|
99
115
|
|
|
100
116
|
### CLI Options
|
|
101
117
|
|
|
102
118
|
```bash
|
|
103
|
-
svgfusion
|
|
119
|
+
svgfusion <input> [options]
|
|
104
120
|
|
|
105
121
|
Options:
|
|
106
122
|
-o, --output <output> Output directory (default: "./components")
|
|
107
123
|
-f, --framework <framework> Target framework (react|vue) (default: "react")
|
|
108
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")
|
|
109
129
|
--no-optimize Skip SVG optimization
|
|
110
130
|
--prefix <prefix> Add prefix to component name (sanitized)
|
|
111
131
|
--suffix <suffix> Add suffix to component name (sanitized)
|
|
@@ -118,17 +138,25 @@ Perfect for trying out SVGFusion or one-time conversions:
|
|
|
118
138
|
|
|
119
139
|
```bash
|
|
120
140
|
# Convert React components
|
|
121
|
-
npx svgfusion
|
|
141
|
+
npx svgfusion ./assets/icons --output ./src/components/icons
|
|
122
142
|
|
|
123
143
|
# Convert Vue components with TypeScript
|
|
124
|
-
npx svgfusion
|
|
144
|
+
npx svgfusion ./assets/icons --output ./src/components --framework vue --typescript
|
|
125
145
|
|
|
126
146
|
# Convert single file
|
|
127
|
-
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
|
|
128
154
|
```
|
|
129
155
|
|
|
130
156
|
### Programmatic Usage
|
|
131
157
|
|
|
158
|
+
#### Single File Conversion
|
|
159
|
+
|
|
132
160
|
```typescript
|
|
133
161
|
import { convertToReact, convertToVue, readSvgFile } from 'svgfusion';
|
|
134
162
|
|
|
@@ -154,6 +182,41 @@ console.log(reactResult.code); // Generated React component
|
|
|
154
182
|
console.log(vueResult.code); // Generated Vue component
|
|
155
183
|
```
|
|
156
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
|
+
|
|
157
220
|
## API Reference
|
|
158
221
|
|
|
159
222
|
### `convertToReact(svgContent, options)`
|
|
@@ -184,6 +247,39 @@ Convert SVG to Vue 3 component. **Note: This is a synchronous function.**
|
|
|
184
247
|
- `compositionApi?: boolean` - Use Composition API (default: `true`)
|
|
185
248
|
- `optimize?: boolean` - Apply SVGO optimization (default: `true`)
|
|
186
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
|
+
|
|
187
283
|
### `optimizeSvg(svgContent, config?)`
|
|
188
284
|
|
|
189
285
|
Optimize SVG content using SVGO. **Note: This is a synchronous function.**
|
|
@@ -284,7 +380,7 @@ const customConfig = createSvgoConfig({
|
|
|
284
380
|
const optimizedSvg = optimizeSvg(svgContent, customConfig);
|
|
285
381
|
```
|
|
286
382
|
|
|
287
|
-
### Batch Processing
|
|
383
|
+
### Manual Batch Processing
|
|
288
384
|
|
|
289
385
|
```typescript
|
|
290
386
|
import {
|
|
@@ -306,6 +402,65 @@ for (const svgFile of svgFiles) {
|
|
|
306
402
|
}
|
|
307
403
|
```
|
|
308
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
|
+
|
|
309
464
|
## Complex Filename Support
|
|
310
465
|
|
|
311
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
|
|
@@ -520,43 +671,11 @@ async function convertToVue(svgContent, options = {}) {
|
|
|
520
671
|
var import_url = require("url");
|
|
521
672
|
var import_path3 = require("path");
|
|
522
673
|
var import_fs2 = require("fs");
|
|
674
|
+
|
|
675
|
+
// src/utils/banner.ts
|
|
676
|
+
init_cjs_shims();
|
|
523
677
|
var import_figlet = __toESM(require("figlet"));
|
|
524
|
-
|
|
525
|
-
cyan: "\x1B[36m",
|
|
526
|
-
blue: "\x1B[34m",
|
|
527
|
-
green: "\x1B[32m",
|
|
528
|
-
red: "\x1B[31m",
|
|
529
|
-
gray: "\x1B[90m",
|
|
530
|
-
dim: "\x1B[2m",
|
|
531
|
-
// Bold gradient colors with dramatic artistic accents
|
|
532
|
-
darkBlue: "\x1B[38;2;0;100;255m",
|
|
533
|
-
// Electric blue (bold)
|
|
534
|
-
mediumBlue: "\x1B[38;2;50;150;255m",
|
|
535
|
-
// Bright blue
|
|
536
|
-
lightBlue: "\x1B[38;2;100;200;255m",
|
|
537
|
-
// Vivid sky blue
|
|
538
|
-
blueCyan: "\x1B[38;2;0;150;255m",
|
|
539
|
-
// Electric blue-cyan
|
|
540
|
-
mediumCyan: "\x1B[38;2;0;255;255m",
|
|
541
|
-
// Pure cyan (bold)
|
|
542
|
-
darkCyan: "\x1B[38;2;0;200;200m",
|
|
543
|
-
// Electric turquoise
|
|
544
|
-
softTeal: "\x1B[38;2;0;255;150m",
|
|
545
|
-
// Electric teal
|
|
546
|
-
tealGreen: "\x1B[38;2;0;200;100m",
|
|
547
|
-
// Vivid teal green
|
|
548
|
-
softGreen: "\x1B[38;2;50;255;100m",
|
|
549
|
-
// Electric green
|
|
550
|
-
mediumGreen: "\x1B[38;2;0;255;50m",
|
|
551
|
-
// Pure green (bold)
|
|
552
|
-
reset: "\x1B[0m"
|
|
553
|
-
};
|
|
554
|
-
var __filename2 = (0, import_url.fileURLToPath)(importMetaUrl);
|
|
555
|
-
var __dirname = (0, import_path3.dirname)(__filename2);
|
|
556
|
-
var packageJson = JSON.parse(
|
|
557
|
-
(0, import_fs2.readFileSync)((0, import_path3.join)(__dirname, "..", "package.json"), "utf-8")
|
|
558
|
-
);
|
|
559
|
-
function createBanner() {
|
|
678
|
+
function createBanner(colors2) {
|
|
560
679
|
const title = import_figlet.default.textSync("SVGfusion", {
|
|
561
680
|
font: "ANSI Shadow",
|
|
562
681
|
horizontalLayout: "default",
|
|
@@ -576,25 +695,25 @@ function createBanner() {
|
|
|
576
695
|
return char;
|
|
577
696
|
}
|
|
578
697
|
if (diagonalProgress <= 0.1) {
|
|
579
|
-
return `${
|
|
698
|
+
return `${colors2.darkBlue}${char}${colors2.reset}`;
|
|
580
699
|
} else if (diagonalProgress <= 0.2) {
|
|
581
|
-
return `${
|
|
700
|
+
return `${colors2.lightBlue}${char}${colors2.reset}`;
|
|
582
701
|
} else if (diagonalProgress <= 0.32) {
|
|
583
|
-
return `${
|
|
702
|
+
return `${colors2.mediumBlue}${char}${colors2.reset}`;
|
|
584
703
|
} else if (diagonalProgress <= 0.42) {
|
|
585
|
-
return `${
|
|
704
|
+
return `${colors2.blueCyan}${char}${colors2.reset}`;
|
|
586
705
|
} else if (diagonalProgress <= 0.52) {
|
|
587
|
-
return `${
|
|
706
|
+
return `${colors2.mediumCyan}${char}${colors2.reset}`;
|
|
588
707
|
} else if (diagonalProgress <= 0.62) {
|
|
589
|
-
return `${
|
|
708
|
+
return `${colors2.darkCyan}${char}${colors2.reset}`;
|
|
590
709
|
} else if (diagonalProgress <= 0.72) {
|
|
591
|
-
return `${
|
|
710
|
+
return `${colors2.softTeal}${char}${colors2.reset}`;
|
|
592
711
|
} else if (diagonalProgress <= 0.82) {
|
|
593
|
-
return `${
|
|
712
|
+
return `${colors2.tealGreen}${char}${colors2.reset}`;
|
|
594
713
|
} else if (diagonalProgress <= 0.92) {
|
|
595
|
-
return `${
|
|
714
|
+
return `${colors2.softGreen}${char}${colors2.reset}`;
|
|
596
715
|
} else {
|
|
597
|
-
return `${
|
|
716
|
+
return `${colors2.mediumGreen}${char}${colors2.reset}`;
|
|
598
717
|
}
|
|
599
718
|
}).join("");
|
|
600
719
|
return processedLine;
|
|
@@ -602,30 +721,89 @@ function createBanner() {
|
|
|
602
721
|
return `
|
|
603
722
|
${gradientTitle}
|
|
604
723
|
|
|
605
|
-
${
|
|
606
|
-
${
|
|
724
|
+
${colors2.gray}Transform SVG files into production-ready components${colors2.reset}
|
|
725
|
+
${colors2.blue}React${colors2.reset} ${colors2.gray}\u2022${colors2.reset} ${colors2.green}Vue 3${colors2.reset} ${colors2.gray}\u2022${colors2.reset} ${colors2.blue}TypeScript${colors2.reset}
|
|
607
726
|
|
|
608
|
-
${
|
|
727
|
+
${colors2.gray}\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500${colors2.reset}
|
|
609
728
|
`;
|
|
610
729
|
}
|
|
730
|
+
|
|
731
|
+
// src/utils/colors.ts
|
|
732
|
+
init_cjs_shims();
|
|
733
|
+
var colors = {
|
|
734
|
+
cyan: "\x1B[36m",
|
|
735
|
+
blue: "\x1B[34m",
|
|
736
|
+
green: "\x1B[32m",
|
|
737
|
+
red: "\x1B[31m",
|
|
738
|
+
gray: "\x1B[90m",
|
|
739
|
+
dim: "\x1B[2m",
|
|
740
|
+
// Bold gradient colors with dramatic artistic accents
|
|
741
|
+
darkBlue: "\x1B[38;2;0;100;255m",
|
|
742
|
+
// Electric blue (bold)
|
|
743
|
+
mediumBlue: "\x1B[38;2;50;150;255m",
|
|
744
|
+
// Bright blue
|
|
745
|
+
lightBlue: "\x1B[38;2;100;200;255m",
|
|
746
|
+
// Vivid sky blue
|
|
747
|
+
blueCyan: "\x1B[38;2;0;150;255m",
|
|
748
|
+
// Electric blue-cyan
|
|
749
|
+
mediumCyan: "\x1B[38;2;0;255;255m",
|
|
750
|
+
// Pure cyan (bold)
|
|
751
|
+
darkCyan: "\x1B[38;2;0;200;200m",
|
|
752
|
+
// Electric turquoise
|
|
753
|
+
softTeal: "\x1B[38;2;0;255;150m",
|
|
754
|
+
// Electric teal
|
|
755
|
+
tealGreen: "\x1B[38;2;0;200;100m",
|
|
756
|
+
// Vivid teal green
|
|
757
|
+
softGreen: "\x1B[38;2;50;255;100m",
|
|
758
|
+
// Electric green
|
|
759
|
+
mediumGreen: "\x1B[38;2;0;255;50m",
|
|
760
|
+
// Pure green (bold)
|
|
761
|
+
reset: "\x1B[0m"
|
|
762
|
+
};
|
|
763
|
+
|
|
764
|
+
// src/cli.ts
|
|
765
|
+
var __filename2 = (0, import_url.fileURLToPath)(importMetaUrl);
|
|
766
|
+
var __dirname = (0, import_path3.dirname)(__filename2);
|
|
767
|
+
var packageJson = JSON.parse(
|
|
768
|
+
(0, import_fs2.readFileSync)((0, import_path3.join)(__dirname, "..", "package.json"), "utf-8")
|
|
769
|
+
);
|
|
611
770
|
var program = new import_commander.Command();
|
|
612
771
|
var isNpxCall = process.argv[1]?.includes("npx") || process.argv[1]?.includes("_npx");
|
|
613
|
-
if (process.argv.length ===
|
|
614
|
-
console.log(createBanner());
|
|
772
|
+
if (process.argv.length === 1 || isNpxCall && process.argv.length === 2) {
|
|
773
|
+
console.log(createBanner(colors));
|
|
615
774
|
}
|
|
616
775
|
program.name("svgfusion").description(
|
|
617
776
|
"Transform SVG files into production-ready React and Vue 3 components"
|
|
618
777
|
).version(packageJson.version);
|
|
619
|
-
program.
|
|
778
|
+
program.argument("[input]", "Input SVG file or directory").option("-o, --output <output>", "Output directory", "./components").option(
|
|
620
779
|
"-f, --framework <framework>",
|
|
621
780
|
"Target framework (react|vue)",
|
|
622
781
|
"react"
|
|
623
|
-
).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(
|
|
624
787
|
async (input, options) => {
|
|
625
|
-
|
|
788
|
+
if (!input) {
|
|
789
|
+
program.outputHelp();
|
|
790
|
+
process.exit(0);
|
|
791
|
+
}
|
|
792
|
+
console.log(createBanner(colors));
|
|
626
793
|
console.log(`${colors.blue}\u{1F504} Processing SVG files...${colors.reset}`);
|
|
627
794
|
try {
|
|
628
|
-
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;
|
|
629
807
|
if (framework !== "react" && framework !== "vue") {
|
|
630
808
|
throw new Error('Framework must be either "react" or "vue"');
|
|
631
809
|
}
|
|
@@ -638,7 +816,7 @@ program.command("convert").description("Convert SVG files to React or Vue compon
|
|
|
638
816
|
throw new Error("Input file must be an SVG file");
|
|
639
817
|
}
|
|
640
818
|
} else if (inputStat.isDirectory()) {
|
|
641
|
-
svgFiles = await readSvgDirectory(input);
|
|
819
|
+
svgFiles = await readSvgDirectory(input, recursive);
|
|
642
820
|
} else {
|
|
643
821
|
throw new Error("Input must be a file or directory");
|
|
644
822
|
}
|
|
@@ -648,6 +826,7 @@ program.command("convert").description("Convert SVG files to React or Vue compon
|
|
|
648
826
|
console.log(
|
|
649
827
|
`${colors.blue}\u{1F504} Converting ${svgFiles.length} SVG file(s)...${colors.reset}`
|
|
650
828
|
);
|
|
829
|
+
const results = [];
|
|
651
830
|
for (const filePath of svgFiles) {
|
|
652
831
|
const svgContent = await readSvgFile(filePath);
|
|
653
832
|
const optimizedSvg = optimize2 ? optimizeSvg(svgContent) : svgContent;
|
|
@@ -666,6 +845,21 @@ program.command("convert").description("Convert SVG files to React or Vue compon
|
|
|
666
845
|
});
|
|
667
846
|
const outputPath = (0, import_path3.join)(output, result.filename);
|
|
668
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
|
+
);
|
|
669
863
|
}
|
|
670
864
|
console.log(
|
|
671
865
|
`${colors.green}\u2705 Successfully converted ${svgFiles.length} SVG file(s) to ${framework} components${colors.reset}`
|
|
@@ -673,6 +867,10 @@ program.command("convert").description("Convert SVG files to React or Vue compon
|
|
|
673
867
|
console.log(
|
|
674
868
|
`${colors.dim}\u{1F4C1} Output location: ${colors.reset}${colors.cyan}${output}${colors.reset}`
|
|
675
869
|
);
|
|
870
|
+
const componentNames = results.map((r) => r.componentName);
|
|
871
|
+
console.log(
|
|
872
|
+
`${colors.dim}\u{1F4E6} Generated components: ${colors.reset}${componentNames.join(", ")}`
|
|
873
|
+
);
|
|
676
874
|
} catch (error) {
|
|
677
875
|
console.error(
|
|
678
876
|
`${colors.red}\u274C Error: ${error instanceof Error ? error.message : "Unknown error"}${colors.reset}`
|
|
@@ -680,5 +878,19 @@ program.command("convert").description("Convert SVG files to React or Vue compon
|
|
|
680
878
|
process.exit(1);
|
|
681
879
|
}
|
|
682
880
|
}
|
|
881
|
+
).addHelpText("before", createBanner(colors)).addHelpText(
|
|
882
|
+
"after",
|
|
883
|
+
`
|
|
884
|
+
${colors.gray}Examples:${colors.reset}
|
|
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}
|
|
890
|
+
`
|
|
683
891
|
);
|
|
892
|
+
if (process.argv.length === 2) {
|
|
893
|
+
program.outputHelp();
|
|
894
|
+
process.exit(0);
|
|
895
|
+
}
|
|
684
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.0",
|
|
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",
|