svgfusion 1.0.0-beta.4 → 1.0.0-beta.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,6 +1,8 @@
1
1
  # SVGFusion
2
2
 
3
- **Transform SVG files into production-ready React and Vue 3 components with TypeScript support, automatic optimization, and intelligent naming.**
3
+ **Transform SVG files into production-ready React and Vue 3 components with TypeScript support and automatic optimization.**
4
+
5
+ A powerful CLI tool and library that converts SVG files into optimized React and Vue components with built-in TypeScript support, smart naming conventions, and flexible configuration options.
4
6
 
5
7
  [![npm version](https://badge.fury.io/js/svgfusion.svg)](https://badge.fury.io/js/svgfusion)
6
8
  [![TypeScript](https://img.shields.io/badge/TypeScript-007ACC?style=flat&logo=typescript&logoColor=white)](https://www.typescriptlang.org/)
@@ -17,12 +19,21 @@
17
19
  - **Flexible API**: Both CLI and programmatic usage
18
20
  - **Batch Processing**: Convert entire directories of SVG files
19
21
  - **Complex Filenames**: Handles design system metadata and special characters
22
+ - **Zero Configuration**: Works out of the box with sensible defaults
23
+ - **Production Ready**: Optimized output with proper TypeScript types
20
24
 
21
25
  ## Quick Start
22
26
 
23
27
  ### Installation
24
28
 
25
29
  ```bash
30
+ # Install globally (recommended for CLI usage)
31
+ npm install -g svgfusion
32
+
33
+ # Or use npx (no installation needed)
34
+ npx svgfusion convert ./icons --output ./components
35
+
36
+ # Or install locally for programmatic usage
26
37
  npm install svgfusion
27
38
  # or
28
39
  yarn add svgfusion
@@ -33,23 +44,57 @@ pnpm add svgfusion
33
44
  ### CLI Usage
34
45
 
35
46
  ```bash
36
- # Convert to React components
37
- svgfusion react ./icons --out ./components/react
47
+ # Convert to React components (default)
48
+ svgfusion convert ./icons --output ./components
38
49
 
39
50
  # Convert to Vue 3 components
40
- svgfusion vue ./icons --out ./components/vue
51
+ svgfusion convert ./icons --output ./components --framework vue
52
+
53
+ # Single file conversion with TypeScript
54
+ svgfusion convert ./star.svg --output ./components --typescript
55
+
56
+ # Skip optimization
57
+ svgfusion convert ./icons --output ./components --no-optimize
58
+
59
+ # Using npx (no global install needed)
60
+ npx svgfusion convert ./icons --output ./components --framework react
61
+ ```
62
+
63
+ ### CLI Options
41
64
 
42
- # Single file conversion
43
- svgfusion react ./star.svg --name StarIcon --out ./components
65
+ ```bash
66
+ svgfusion convert <input> [options]
67
+
68
+ Options:
69
+ -o, --output <output> Output directory (default: "./components")
70
+ -f, --framework <framework> Target framework (react|vue) (default: "react")
71
+ -t, --typescript Generate TypeScript files
72
+ --no-optimize Skip SVG optimization
73
+ -h, --help Show help
74
+ ```
75
+
76
+ ### Using with npx (No Installation Required)
77
+
78
+ Perfect for trying out SVGFusion or one-time conversions:
44
79
 
45
- # With optimization and prefix
46
- svgfusion react ./icons --out ./components --optimize --prefix Icon
80
+ ```bash
81
+ # Convert React components
82
+ npx svgfusion convert ./assets/icons --output ./src/components/icons
83
+
84
+ # Convert Vue components with TypeScript
85
+ npx svgfusion convert ./assets/icons --output ./src/components --framework vue --typescript
86
+
87
+ # Convert single file
88
+ npx svgfusion convert ./logo.svg --output ./src/components --framework react
47
89
  ```
48
90
 
49
91
  ### Programmatic Usage
50
92
 
51
93
  ```typescript
52
- import { convertToReact, convertToVue } from 'svgfusion';
94
+ import { convertToReact, convertToVue, readSvgFile } from 'svgfusion';
95
+
96
+ // Read SVG file
97
+ const svgContent = await readSvgFile('./icons/star.svg');
53
98
 
54
99
  // React conversion
55
100
  const reactResult = await convertToReact(svgContent, {
@@ -60,7 +105,7 @@ const reactResult = await convertToReact(svgContent, {
60
105
  });
61
106
 
62
107
  // Vue conversion
63
- const vueResult = await convertToVue(svgContent, {
108
+ const vueResult = convertToVue(svgContent, {
64
109
  name: 'StarIcon',
65
110
  typescript: true,
66
111
  scriptSetup: true,
@@ -88,7 +133,7 @@ Convert SVG to React component.
88
133
 
89
134
  ### `convertToVue(svgContent, options)`
90
135
 
91
- Convert SVG to Vue 3 component.
136
+ Convert SVG to Vue 3 component. **Note: This is a synchronous function.**
92
137
 
93
138
  **Options:**
94
139
 
@@ -102,14 +147,14 @@ Convert SVG to Vue 3 component.
102
147
 
103
148
  ### `optimizeSvg(svgContent, config?)`
104
149
 
105
- Optimize SVG content using SVGO.
150
+ Optimize SVG content using SVGO. **Note: This is a synchronous function.**
106
151
 
107
152
  ### File Utilities
108
153
 
109
- - `readSvgFile(filePath)` - Read SVG file
110
- - `writeSvgFile(filePath, content)` - Write SVG file
111
- - `readSvgDirectory(dirPath, recursive?)` - Read SVG files from directory
112
- - `writeComponentFile(filePath, content)` - Write component file
154
+ - `readSvgFile(filePath)` - Read SVG file (async)
155
+ - `writeSvgFile(filePath, content)` - Write SVG file (async)
156
+ - `readSvgDirectory(dirPath, recursive?)` - Read SVG files from directory (async)
157
+ - `writeComponentFile(filePath, content)` - Write component file (async)
113
158
 
114
159
  ## Examples
115
160
 
package/dist/cli.d.ts CHANGED
@@ -1 +1,2 @@
1
- #!/usr/bin/env node
1
+
2
+ export { }
package/dist/cli.js CHANGED
@@ -1,56 +1,281 @@
1
1
  #!/usr/bin/env node
2
- 'use strict';
3
-
4
- var commander = require('commander');
5
- var promises = require('fs/promises');
6
- var path = require('path');
7
- var fs = require('fs');
8
- var core = require('@svgr/core');
9
- var svgo = require('svgo');
10
- var url = require('url');
11
-
12
- var A=()=>typeof document>"u"?new URL("file:"+__filename).href:document.currentScript&&document.currentScript.src||new URL("main.js",document.baseURI).href,u=A();async function R(e){try{return await promises.readFile(e,"utf-8")}catch(t){throw new Error(`Failed to read SVG file: ${e}. ${t}`)}}async function T(e,t){try{await H(path.dirname(e)),await promises.writeFile(e,t,"utf-8");}catch(r){throw new Error(`Failed to write component file: ${e}. ${r}`)}}async function x(e,t=!1){try{let r=await promises.readdir(e),n=[];for(let s of r){let i=path.join(e,s),o=await promises.stat(i);if(o.isDirectory()&&t){let p=await x(i,t);n.push(...p);}else o.isFile()&&path.extname(s).toLowerCase()===".svg"&&n.push(i);}return n}catch(r){throw new Error(`Failed to read directory: ${e}. ${r}`)}}async function H(e){fs.existsSync(e)||await promises.mkdir(e,{recursive:!0});}function d(e,t=!0){return e==="react"?t?".tsx":".jsx":".vue"}function w(e,t,r){return `${t}${r}`}var Q={plugins:[{name:"preset-default",params:{overrides:{removeViewBox:!1,removeTitle:!1,removeDesc:!1,removeUselessStrokeAndFill:!1,convertColors:{currentColor:!0,names2hex:!0,rgb2hex:!0,shorthex:!0,shortname:!0}}}},"removeDimensions","cleanupNumericValues"]};function f(e,t=Q){try{return svgo.optimize(e,t).data}catch(r){throw new Error(`Failed to optimize SVG: ${r}`)}}function C(e){return e.replace(/[^a-zA-Z0-9\s-_]/g," ").split(/[\s-_]+/).filter(t=>t.length>0).map(t=>t.charAt(0).toUpperCase()+t.slice(1).toLowerCase()).join("")}function $(e,t,r){let n=t?C(t):"",s=r?C(r):"",i=C(e);return `${n}${i}${s}`}async function z(e,t={}){let{name:r,prefix:n,suffix:s,optimize:i=!0,typescript:o=!0,memo:p=!0,ref:m=!0,titleProp:l=!0,descProp:g=!0}=t;try{let a=e;i&&(a=f(e));let c=$(r||"icon",n,s),v=await core.transform(a,{typescript:o,memo:p,ref:m,titleProp:l,descProp:g,svgProps:{className:"{className}"}},{componentName:c}),h=o?`import { SVGProps } from 'react';
13
- `:"",y=p?`import { memo } from 'react';
14
- `:"",S=m?`import { forwardRef } from 'react';
15
- `:"",D=`
16
- export default ${c};
17
- export { ${c} };`,G=o?"SVGProps<SVGSVGElement> & { className?: string; }":"",N=o?`props: ${G}`:"props",k=p?"memo(":"",_=m?`forwardRef<SVGSVGElement, ${G}>(`:"";v=`${h}${y}${S}
18
- const ${c} = ${k}${_}(${N}) => {
19
- return ${v};
20
- }${`${m?")":""}${p?")":""}`};
21
-
22
- ${c}.displayName = "${c}";
23
- ${D}`;let O=d("react",o),j=w("icon.svg",c,O);return {code:v,filename:j,componentName:c}}catch(a){throw new Error(`Failed to convert SVG to React: ${a}`)}}function E(e,t={}){let{name:r,prefix:n,suffix:s,optimize:i=!0,typescript:o=!0,compositionApi:p=!0,scriptSetup:m=!0}=t;try{let l=e;i&&(l=f(e));let a=$(r||"icon",n,s),F=l.replace(/<\?xml[^>]*\?>\s*/,"").replace(/xmlns="[^"]*"/g,"").replace(/width="[^"]*"/g,"").replace(/height="[^"]*"/g,"").replace(/<svg/,'<svg v-bind="$attrs"').replace(/class="([^"]*)"/g,'class="$1"').replace(/currentColor/g,"currentColor"),c=m?Y(o,a):ee(a,o),b=`<template>
24
- ${F}
25
- </template>`,h=`${c}
26
-
27
- ${b}
28
-
29
- <style scoped>
2
+ "use strict";
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") {
11
+ for (let key of __getOwnPropNames(from))
12
+ if (!__hasOwnProp.call(to, key) && key !== except)
13
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14
+ }
15
+ return to;
16
+ };
17
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
18
+ // If the importer is in node compatibility mode or this is not an ESM
19
+ // file that has been converted to a CommonJS file using a Babel-
20
+ // compatible transform (i.e. "__esModule" has not been set), then set
21
+ // "default" to the CommonJS "module.exports" for node compatibility.
22
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
23
+ mod
24
+ ));
25
+
26
+ // node_modules/tsup/assets/cjs_shims.js
27
+ var getImportMetaUrl = () => typeof document === "undefined" ? new URL("file:" + __filename).href : document.currentScript && document.currentScript.src || new URL("main.js", document.baseURI).href;
28
+ var importMetaUrl = /* @__PURE__ */ getImportMetaUrl();
29
+
30
+ // src/cli.ts
31
+ var import_commander = require("commander");
32
+
33
+ // src/utils/files.ts
34
+ var import_promises = require("fs/promises");
35
+ var import_path = require("path");
36
+ var import_fs = require("fs");
37
+ async function readSvgFile(filePath) {
38
+ try {
39
+ const content = await (0, import_promises.readFile)(filePath, "utf-8");
40
+ return content;
41
+ } catch (error) {
42
+ throw new Error(`Failed to read SVG file: ${filePath}. ${error}`);
43
+ }
44
+ }
45
+ async function writeComponentFile(filePath, content) {
46
+ try {
47
+ await ensureDirectoryExists((0, import_path.dirname)(filePath));
48
+ await (0, import_promises.writeFile)(filePath, content, "utf-8");
49
+ } catch (error) {
50
+ throw new Error(`Failed to write component file: ${filePath}. ${error}`);
51
+ }
52
+ }
53
+ async function readSvgDirectory(dirPath, recursive = false) {
54
+ try {
55
+ const files = await (0, import_promises.readdir)(dirPath);
56
+ const svgFiles = [];
57
+ for (const file of files) {
58
+ const filePath = (0, import_path.join)(dirPath, file);
59
+ const fileStat = await (0, import_promises.stat)(filePath);
60
+ if (fileStat.isDirectory() && recursive) {
61
+ const nestedFiles = await readSvgDirectory(filePath, recursive);
62
+ svgFiles.push(...nestedFiles);
63
+ } else if (fileStat.isFile() && (0, import_path.extname)(file).toLowerCase() === ".svg") {
64
+ svgFiles.push(filePath);
65
+ }
66
+ }
67
+ return svgFiles;
68
+ } catch (error) {
69
+ throw new Error(`Failed to read directory: ${dirPath}. ${error}`);
70
+ }
71
+ }
72
+ async function ensureDirectoryExists(dirPath) {
73
+ if (!(0, import_fs.existsSync)(dirPath)) {
74
+ await (0, import_promises.mkdir)(dirPath, { recursive: true });
75
+ }
76
+ }
77
+ function getFileExtension(framework, typescript = true) {
78
+ if (framework === "react") {
79
+ return typescript ? ".tsx" : ".jsx";
80
+ } else {
81
+ return typescript ? ".vue" : ".vue";
82
+ }
83
+ }
84
+ function getComponentFilename(_svgFilename, componentName, extension) {
85
+ return `${componentName}${extension}`;
86
+ }
87
+
88
+ // src/converters/react.ts
89
+ var import_core = require("@svgr/core");
90
+
91
+ // src/utils/svgo.ts
92
+ var import_svgo = require("svgo");
93
+ var defaultConfig = {
94
+ plugins: [
95
+ {
96
+ name: "preset-default",
97
+ params: {
98
+ overrides: {
99
+ removeViewBox: false,
100
+ removeTitle: false,
101
+ removeDesc: false,
102
+ removeUselessStrokeAndFill: false,
103
+ convertColors: {
104
+ currentColor: true,
105
+ names2hex: true,
106
+ rgb2hex: true,
107
+ shorthex: true,
108
+ // cspell:disable-line
109
+ shortname: true
110
+ }
111
+ }
112
+ }
113
+ },
114
+ "removeDimensions",
115
+ "cleanupNumericValues"
116
+ ]
117
+ };
118
+ function optimizeSvg(svgContent, config = defaultConfig) {
119
+ try {
120
+ const result = (0, import_svgo.optimize)(svgContent, config);
121
+ return result.data;
122
+ } catch (error) {
123
+ throw new Error(`Failed to optimize SVG: ${error}`);
124
+ }
125
+ }
126
+
127
+ // src/utils/name.ts
128
+ function pascalCase(str) {
129
+ return str.replace(/[^a-zA-Z0-9\s-_]/g, " ").split(/[\s-_]+/).filter((word) => word.length > 0).map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join("");
130
+ }
131
+ function formatComponentName(name, prefix, suffix) {
132
+ const prefixPart = prefix ? pascalCase(prefix) : "";
133
+ const suffixPart = suffix ? pascalCase(suffix) : "";
134
+ const baseName = pascalCase(name);
135
+ return `${prefixPart}${baseName}${suffixPart}`;
136
+ }
137
+
138
+ // src/converters/react.ts
139
+ async function convertToReact(svgContent, options = {}) {
140
+ const {
141
+ name,
142
+ prefix,
143
+ suffix,
144
+ optimize: optimize2 = true,
145
+ typescript = true,
146
+ memo = true,
147
+ ref = true,
148
+ titleProp = true,
149
+ descProp = true
150
+ } = options;
151
+ try {
152
+ let processedSvg = svgContent;
153
+ if (optimize2) {
154
+ processedSvg = optimizeSvg(svgContent);
155
+ }
156
+ const baseName = name || "icon";
157
+ const componentName = formatComponentName(baseName, prefix, suffix);
158
+ const svgrOptions = {
159
+ typescript,
160
+ memo,
161
+ ref,
162
+ titleProp,
163
+ descProp,
164
+ svgProps: {
165
+ className: "{className}"
166
+ }
167
+ };
168
+ let result = await (0, import_core.transform)(processedSvg, svgrOptions, {
169
+ componentName
170
+ });
171
+ const typeImports = typescript ? `import { SVGProps } from 'react';
172
+ ` : "";
173
+ const memoImport = memo ? `import { memo } from 'react';
174
+ ` : "";
175
+ const refImport = ref ? `import { forwardRef } from 'react';
176
+ ` : "";
177
+ const exports = `
178
+ export default ${componentName};
179
+ export { ${componentName} };`;
180
+ const propsType = typescript ? `SVGProps<SVGSVGElement> & { className?: string; }` : "";
181
+ const componentProps = typescript ? `props: ${propsType}` : "props";
182
+ const componentFunc = memo ? `memo(` : "";
183
+ const refWrapper = ref ? `forwardRef<SVGSVGElement, ${propsType}>(` : "";
184
+ const closingWrappers = `${ref ? ")" : ""}${memo ? ")" : ""}`;
185
+ result = `${typeImports}${memoImport}${refImport}
186
+ const ${componentName} = ${componentFunc}${refWrapper}(${componentProps}) => {
187
+ return ${result};
188
+ }${closingWrappers};
189
+
190
+ ${componentName}.displayName = "${componentName}";
191
+ ${exports}`;
192
+ const extension = getFileExtension("react", typescript);
193
+ const filename = getComponentFilename("icon.svg", componentName, extension);
194
+ return {
195
+ code: result,
196
+ filename,
197
+ componentName
198
+ };
199
+ } catch (error) {
200
+ throw new Error(`Failed to convert SVG to React: ${error}`);
201
+ }
202
+ }
203
+
204
+ // src/converters/vue.ts
205
+ function convertToVue(svgContent, options = {}) {
206
+ const {
207
+ name,
208
+ prefix,
209
+ suffix,
210
+ optimize: optimize2 = true,
211
+ typescript = true,
212
+ compositionApi: _compositionApi = true,
213
+ // eslint-disable-line @typescript-eslint/no-unused-vars
214
+ scriptSetup = true
215
+ } = options;
216
+ try {
217
+ let processedSvg = svgContent;
218
+ if (optimize2) {
219
+ processedSvg = optimizeSvg(svgContent);
220
+ }
221
+ const baseName = name || "icon";
222
+ const componentName = formatComponentName(baseName, prefix, suffix);
223
+ const cleanedSvg = processedSvg.replace(/<\?xml[^>]*\?>\s*/, "").replace(/xmlns="[^"]*"/g, "").replace(/width="[^"]*"/g, "").replace(/height="[^"]*"/g, "").replace(/<svg/, '<svg v-bind="$attrs"').replace(/class="([^"]*)"/g, 'class="$1"').replace(/currentColor/g, "currentColor");
224
+ const scriptTag = scriptSetup ? generateScriptSetup(typescript, componentName) : generateCompositionScript(componentName, typescript);
225
+ const template = `<template>
226
+ ${cleanedSvg}
227
+ </template>`;
228
+ const style = `<style scoped>
30
229
  /* Add component-specific styles here */
31
- </style>`,y=d("vue",o),S=w("icon.svg",a,y);return {code:h,filename:S,componentName:a}}catch(l){throw new Error(`Failed to convert SVG to Vue: ${l}`)}}function Y(e,t){return `<script setup${e?' lang="ts"':""}>${e?`
230
+ </style>`;
231
+ const vueComponent = `${scriptTag}
232
+
233
+ ${template}
234
+
235
+ ${style}`;
236
+ const extension = getFileExtension("vue", typescript);
237
+ const filename = getComponentFilename("icon.svg", componentName, extension);
238
+ return {
239
+ code: vueComponent,
240
+ filename,
241
+ componentName
242
+ };
243
+ } catch (error) {
244
+ throw new Error(`Failed to convert SVG to Vue: ${error}`);
245
+ }
246
+ }
247
+ function generateScriptSetup(typescript, componentName) {
248
+ const lang = typescript ? ' lang="ts"' : "";
249
+ const propsType = typescript ? `
32
250
  interface Props {
33
251
  class?: string;
34
252
  style?: string | Record<string, any>;
35
- }`:""}${e?`
253
+ }` : "";
254
+ const propsDefinition = typescript ? `
36
255
  const props = withDefaults(defineProps<Props>(), {
37
256
  class: '',
38
257
  style: undefined,
39
- });`:`
258
+ });` : `
40
259
  const props = withDefaults(defineProps(), {
41
260
  class: '',
42
261
  style: undefined,
43
- });`}
262
+ });`;
263
+ return `<script setup${lang}>${propsType}${propsDefinition}
44
264
 
45
265
  // Component name for debugging
46
- const __name = '${t}';
47
- </script>`}function ee(e,t){let r=t?' lang="ts"':"",n=t?`
266
+ const __name = '${componentName}';
267
+ </script>`;
268
+ }
269
+ function generateCompositionScript(componentName, typescript) {
270
+ const lang = typescript ? ' lang="ts"' : "";
271
+ const propsType = typescript ? `
48
272
  interface Props {
49
273
  class?: string;
50
274
  style?: string | Record<string, any>;
51
- }`:"",s=t?`
52
- const ${e} = defineComponent({
53
- name: '${e}',
275
+ }` : "";
276
+ const exportStatement = typescript ? `
277
+ const ${componentName} = defineComponent({
278
+ name: '${componentName}',
54
279
  props: {
55
280
  class: {
56
281
  type: String,
@@ -66,10 +291,10 @@ const ${e} = defineComponent({
66
291
  },
67
292
  });
68
293
 
69
- export default ${e};
70
- export { ${e} };`:`
71
- const ${e} = defineComponent({
72
- name: '${e}',
294
+ export default ${componentName};
295
+ export { ${componentName} };` : `
296
+ const ${componentName} = defineComponent({
297
+ name: '${componentName}',
73
298
  props: {
74
299
  class: {
75
300
  type: String,
@@ -85,7 +310,78 @@ const ${e} = defineComponent({
85
310
  },
86
311
  });
87
312
 
88
- export default ${e};
89
- export { ${e} };`;return `<script${r}>
90
- import { defineComponent } from 'vue';${n}${s}
91
- </script>`}var se=url.fileURLToPath(u),ie=path.dirname(se),ae=JSON.parse(fs.readFileSync(path.join(ie,"..","package.json"),"utf-8")),V=new commander.Command;V.name("svgfusion").description("Transform SVG files into production-ready React and Vue 3 components").version(ae.version);V.command("convert").description("Convert SVG files to React or Vue components").argument("<input>","Input SVG file or directory").option("-o, --output <output>","Output directory","./components").option("-f, --framework <framework>","Target framework (react|vue)","react").option("-t, --typescript","Generate TypeScript files",!1).option("--no-optimize","Skip SVG optimization").action(async(e,t)=>{console.log("\u{1F504} Processing SVG files...");try{let{framework:r,output:n,typescript:s,optimize:i}=t;if(r!=="react"&&r!=="vue")throw new Error('Framework must be either "react" or "vue"');let o=await x(e);if(o.length===0)throw new Error("No SVG files found in the input path");console.log(`\u{1F504} Converting ${o.length} SVG file(s)...`);for(let p of o){let m=await R(p),l=i?f(m):m,g=r==="react"?await z(l,{typescript:s}):E(l,{typescript:s}),a=path.join(n,g.filename);await T(a,g.code);}console.log(`\u2705 Successfully converted ${o.length} SVG file(s) to ${r} components`);}catch(r){console.error(`\u274C Error: ${r instanceof Error?r.message:"Unknown error"}`),process.exit(1);}});V.parse();
313
+ export default ${componentName};
314
+ export { ${componentName} };`;
315
+ return `<script${lang}>
316
+ import { defineComponent } from 'vue';${propsType}${exportStatement}
317
+ </script>`;
318
+ }
319
+
320
+ // src/cli.ts
321
+ var import_url = require("url");
322
+ var import_path2 = require("path");
323
+ var import_fs2 = require("fs");
324
+ var import_figlet = __toESM(require("figlet"));
325
+ var __filename2 = (0, import_url.fileURLToPath)(importMetaUrl);
326
+ var __dirname = (0, import_path2.dirname)(__filename2);
327
+ var packageJson = JSON.parse(
328
+ (0, import_fs2.readFileSync)((0, import_path2.join)(__dirname, "..", "package.json"), "utf-8")
329
+ );
330
+ function createBanner() {
331
+ const title = import_figlet.default.textSync("SVGFUSION", {
332
+ font: "Train",
333
+ horizontalLayout: "fitted",
334
+ verticalLayout: "fitted",
335
+ width: 80,
336
+ whitespaceBreak: true
337
+ });
338
+ return `
339
+ ${title}
340
+
341
+ Transform SVG files into production-ready components
342
+ React \u2022 Vue 3 \u2022 TypeScript
343
+
344
+ \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
345
+ `;
346
+ }
347
+ var program = new import_commander.Command();
348
+ program.name("svgfusion").description(
349
+ "Transform SVG files into production-ready React and Vue 3 components"
350
+ ).version(packageJson.version);
351
+ program.command("convert").description("Convert SVG files to React or Vue components").argument("<input>", "Input SVG file or directory").option("-o, --output <output>", "Output directory", "./components").option(
352
+ "-f, --framework <framework>",
353
+ "Target framework (react|vue)",
354
+ "react"
355
+ ).option("-t, --typescript", "Generate TypeScript files", false).option("--no-optimize", "Skip SVG optimization").action(
356
+ async (input, options) => {
357
+ console.log(createBanner());
358
+ console.log("\u{1F504} Processing SVG files...");
359
+ try {
360
+ const { framework, output, typescript, optimize: optimize2 } = options;
361
+ if (framework !== "react" && framework !== "vue") {
362
+ throw new Error('Framework must be either "react" or "vue"');
363
+ }
364
+ const svgFiles = await readSvgDirectory(input);
365
+ if (svgFiles.length === 0) {
366
+ throw new Error("No SVG files found in the input path");
367
+ }
368
+ console.log(`\u{1F504} Converting ${svgFiles.length} SVG file(s)...`);
369
+ for (const filePath of svgFiles) {
370
+ const svgContent = await readSvgFile(filePath);
371
+ const optimizedSvg = optimize2 ? optimizeSvg(svgContent) : svgContent;
372
+ const result = framework === "react" ? await convertToReact(optimizedSvg, { typescript }) : convertToVue(optimizedSvg, { typescript });
373
+ const outputPath = (0, import_path2.join)(output, result.filename);
374
+ await writeComponentFile(outputPath, result.code);
375
+ }
376
+ console.log(
377
+ `\u2705 Successfully converted ${svgFiles.length} SVG file(s) to ${framework} components`
378
+ );
379
+ } catch (error) {
380
+ console.error(
381
+ `\u274C Error: ${error instanceof Error ? error.message : "Unknown error"}`
382
+ );
383
+ process.exit(1);
384
+ }
385
+ }
386
+ );
387
+ program.parse();
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "svgfusion",
3
- "version": "1.0.0-beta.4",
4
- "description": "Transform SVG files into production-ready React and Vue 3 components with TypeScript support, automatic optimization, and intelligent naming.",
3
+ "version": "1.0.0-beta.6",
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",
7
7
  "types": "dist/index.d.ts",
@@ -53,7 +53,7 @@
53
53
  "url": "https://github.com/lolvoid/svgfusion/issues"
54
54
  },
55
55
  "scripts": {
56
- "build": "tsup",
56
+ "build": "tsup && chmod +x dist/cli.js",
57
57
  "dev": "tsup --watch",
58
58
  "test": "vitest",
59
59
  "test:coverage": "vitest --coverage",
@@ -71,8 +71,10 @@
71
71
  },
72
72
  "dependencies": {
73
73
  "@svgr/core": "^8.0.0",
74
+ "@types/figlet": "^1.7.0",
74
75
  "chalk": "^5.0.0",
75
76
  "commander": "^11.0.0",
77
+ "figlet": "^1.8.1",
76
78
  "ora": "^7.0.0",
77
79
  "svgo": "^3.0.0"
78
80
  },
package/dist/cli.d.mts DELETED
@@ -1 +0,0 @@
1
- #!/usr/bin/env node
package/dist/cli.mjs DELETED
@@ -1,89 +0,0 @@
1
- #!/usr/bin/env node
2
- import { Command } from 'commander';
3
- import { readFile, writeFile, readdir, stat, mkdir } from 'fs/promises';
4
- import { dirname, join, extname } from 'path';
5
- import { readFileSync, existsSync } from 'fs';
6
- import { transform } from '@svgr/core';
7
- import { optimize } from 'svgo';
8
- import { fileURLToPath } from 'url';
9
-
10
- async function z(e){try{return await readFile(e,"utf-8")}catch(t){throw new Error(`Failed to read SVG file: ${e}. ${t}`)}}async function E(e,t){try{await H(dirname(e)),await writeFile(e,t,"utf-8");}catch(r){throw new Error(`Failed to write component file: ${e}. ${r}`)}}async function C(e,t=!1){try{let r=await readdir(e),n=[];for(let s of r){let i=join(e,s),o=await stat(i);if(o.isDirectory()&&t){let p=await C(i,t);n.push(...p);}else o.isFile()&&extname(s).toLowerCase()===".svg"&&n.push(i);}return n}catch(r){throw new Error(`Failed to read directory: ${e}. ${r}`)}}async function H(e){existsSync(e)||await mkdir(e,{recursive:!0});}function $(e,t=!0){return e==="react"?t?".tsx":".jsx":".vue"}function w(e,t,r){return `${t}${r}`}var Q={plugins:[{name:"preset-default",params:{overrides:{removeViewBox:!1,removeTitle:!1,removeDesc:!1,removeUselessStrokeAndFill:!1,convertColors:{currentColor:!0,names2hex:!0,rgb2hex:!0,shorthex:!0,shortname:!0}}}},"removeDimensions","cleanupNumericValues"]};function u(e,t=Q){try{return optimize(e,t).data}catch(r){throw new Error(`Failed to optimize SVG: ${r}`)}}function V(e){return e.replace(/[^a-zA-Z0-9\s-_]/g," ").split(/[\s-_]+/).filter(t=>t.length>0).map(t=>t.charAt(0).toUpperCase()+t.slice(1).toLowerCase()).join("")}function h(e,t,r){let n=t?V(t):"",s=r?V(r):"",i=V(e);return `${n}${i}${s}`}async function P(e,t={}){let{name:r,prefix:n,suffix:s,optimize:i=!0,typescript:o=!0,memo:p=!0,ref:m=!0,titleProp:l=!0,descProp:f=!0}=t;try{let a=e;i&&(a=u(e));let c=h(r||"icon",n,s),g=await transform(a,{typescript:o,memo:p,ref:m,titleProp:l,descProp:f,svgProps:{className:"{className}"}},{componentName:c}),y=o?`import { SVGProps } from 'react';
11
- `:"",S=p?`import { memo } from 'react';
12
- `:"",x=m?`import { forwardRef } from 'react';
13
- `:"",N=`
14
- export default ${c};
15
- export { ${c} };`,T=o?"SVGProps<SVGSVGElement> & { className?: string; }":"",k=o?`props: ${T}`:"props",O=p?"memo(":"",_=m?`forwardRef<SVGSVGElement, ${T}>(`:"";g=`${y}${S}${x}
16
- const ${c} = ${O}${_}(${k}) => {
17
- return ${g};
18
- }${`${m?")":""}${p?")":""}`};
19
-
20
- ${c}.displayName = "${c}";
21
- ${N}`;let A=$("react",o),j=w("icon.svg",c,A);return {code:g,filename:j,componentName:c}}catch(a){throw new Error(`Failed to convert SVG to React: ${a}`)}}function R(e,t={}){let{name:r,prefix:n,suffix:s,optimize:i=!0,typescript:o=!0,compositionApi:p=!0,scriptSetup:m=!0}=t;try{let l=e;i&&(l=u(e));let a=h(r||"icon",n,s),b=l.replace(/<\?xml[^>]*\?>\s*/,"").replace(/xmlns="[^"]*"/g,"").replace(/width="[^"]*"/g,"").replace(/height="[^"]*"/g,"").replace(/<svg/,'<svg v-bind="$attrs"').replace(/class="([^"]*)"/g,'class="$1"').replace(/currentColor/g,"currentColor"),c=m?Y(o,a):ee(a,o),G=`<template>
22
- ${b}
23
- </template>`,y=`${c}
24
-
25
- ${G}
26
-
27
- <style scoped>
28
- /* Add component-specific styles here */
29
- </style>`,S=$("vue",o),x=w("icon.svg",a,S);return {code:y,filename:x,componentName:a}}catch(l){throw new Error(`Failed to convert SVG to Vue: ${l}`)}}function Y(e,t){return `<script setup${e?' lang="ts"':""}>${e?`
30
- interface Props {
31
- class?: string;
32
- style?: string | Record<string, any>;
33
- }`:""}${e?`
34
- const props = withDefaults(defineProps<Props>(), {
35
- class: '',
36
- style: undefined,
37
- });`:`
38
- const props = withDefaults(defineProps(), {
39
- class: '',
40
- style: undefined,
41
- });`}
42
-
43
- // Component name for debugging
44
- const __name = '${t}';
45
- </script>`}function ee(e,t){let r=t?' lang="ts"':"",n=t?`
46
- interface Props {
47
- class?: string;
48
- style?: string | Record<string, any>;
49
- }`:"",s=t?`
50
- const ${e} = defineComponent({
51
- name: '${e}',
52
- props: {
53
- class: {
54
- type: String,
55
- default: '',
56
- },
57
- style: {
58
- type: [String, Object],
59
- default: undefined,
60
- },
61
- },
62
- setup(props: Props) {
63
- return {};
64
- },
65
- });
66
-
67
- export default ${e};
68
- export { ${e} };`:`
69
- const ${e} = defineComponent({
70
- name: '${e}',
71
- props: {
72
- class: {
73
- type: String,
74
- default: '',
75
- },
76
- style: {
77
- type: [String, Object],
78
- default: undefined,
79
- },
80
- },
81
- setup(props) {
82
- return {};
83
- },
84
- });
85
-
86
- export default ${e};
87
- export { ${e} };`;return `<script${r}>
88
- import { defineComponent } from 'vue';${n}${s}
89
- </script>`}var se=fileURLToPath(import.meta.url),ie=dirname(se),ae=JSON.parse(readFileSync(join(ie,"..","package.json"),"utf-8")),F=new Command;F.name("svgfusion").description("Transform SVG files into production-ready React and Vue 3 components").version(ae.version);F.command("convert").description("Convert SVG files to React or Vue components").argument("<input>","Input SVG file or directory").option("-o, --output <output>","Output directory","./components").option("-f, --framework <framework>","Target framework (react|vue)","react").option("-t, --typescript","Generate TypeScript files",!1).option("--no-optimize","Skip SVG optimization").action(async(e,t)=>{console.log("\u{1F504} Processing SVG files...");try{let{framework:r,output:n,typescript:s,optimize:i}=t;if(r!=="react"&&r!=="vue")throw new Error('Framework must be either "react" or "vue"');let o=await C(e);if(o.length===0)throw new Error("No SVG files found in the input path");console.log(`\u{1F504} Converting ${o.length} SVG file(s)...`);for(let p of o){let m=await z(p),l=i?u(m):m,f=r==="react"?await P(l,{typescript:s}):R(l,{typescript:s}),a=join(n,f.filename);await E(a,f.code);}console.log(`\u2705 Successfully converted ${o.length} SVG file(s) to ${r} components`);}catch(r){console.error(`\u274C Error: ${r instanceof Error?r.message:"Unknown error"}`),process.exit(1);}});F.parse();