svgfusion 1.0.0-beta.3 → 1.0.0-beta.5

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,259 @@
1
1
  #!/usr/bin/env node
2
- 'use strict';
2
+ "use strict";
3
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');
4
+ // node_modules/tsup/assets/cjs_shims.js
5
+ var getImportMetaUrl = () => typeof document === "undefined" ? new URL("file:" + __filename).href : document.currentScript && document.currentScript.src || new URL("main.js", document.baseURI).href;
6
+ var importMetaUrl = /* @__PURE__ */ getImportMetaUrl();
11
7
 
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?")":""}`};
8
+ // src/cli.ts
9
+ var import_commander = require("commander");
21
10
 
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}
11
+ // src/utils/files.ts
12
+ var import_promises = require("fs/promises");
13
+ var import_path = require("path");
14
+ var import_fs = require("fs");
15
+ async function readSvgFile(filePath) {
16
+ try {
17
+ const content = await (0, import_promises.readFile)(filePath, "utf-8");
18
+ return content;
19
+ } catch (error) {
20
+ throw new Error(`Failed to read SVG file: ${filePath}. ${error}`);
21
+ }
22
+ }
23
+ async function writeComponentFile(filePath, content) {
24
+ try {
25
+ await ensureDirectoryExists((0, import_path.dirname)(filePath));
26
+ await (0, import_promises.writeFile)(filePath, content, "utf-8");
27
+ } catch (error) {
28
+ throw new Error(`Failed to write component file: ${filePath}. ${error}`);
29
+ }
30
+ }
31
+ async function readSvgDirectory(dirPath, recursive = false) {
32
+ try {
33
+ const files = await (0, import_promises.readdir)(dirPath);
34
+ const svgFiles = [];
35
+ for (const file of files) {
36
+ const filePath = (0, import_path.join)(dirPath, file);
37
+ const fileStat = await (0, import_promises.stat)(filePath);
38
+ if (fileStat.isDirectory() && recursive) {
39
+ const nestedFiles = await readSvgDirectory(filePath, recursive);
40
+ svgFiles.push(...nestedFiles);
41
+ } else if (fileStat.isFile() && (0, import_path.extname)(file).toLowerCase() === ".svg") {
42
+ svgFiles.push(filePath);
43
+ }
44
+ }
45
+ return svgFiles;
46
+ } catch (error) {
47
+ throw new Error(`Failed to read directory: ${dirPath}. ${error}`);
48
+ }
49
+ }
50
+ async function ensureDirectoryExists(dirPath) {
51
+ if (!(0, import_fs.existsSync)(dirPath)) {
52
+ await (0, import_promises.mkdir)(dirPath, { recursive: true });
53
+ }
54
+ }
55
+ function getFileExtension(framework, typescript = true) {
56
+ if (framework === "react") {
57
+ return typescript ? ".tsx" : ".jsx";
58
+ } else {
59
+ return typescript ? ".vue" : ".vue";
60
+ }
61
+ }
62
+ function getComponentFilename(_svgFilename, componentName, extension) {
63
+ return `${componentName}${extension}`;
64
+ }
26
65
 
27
- ${b}
66
+ // src/converters/react.ts
67
+ var import_core = require("@svgr/core");
28
68
 
29
- <style scoped>
69
+ // src/utils/svgo.ts
70
+ var import_svgo = require("svgo");
71
+ var defaultConfig = {
72
+ plugins: [
73
+ {
74
+ name: "preset-default",
75
+ params: {
76
+ overrides: {
77
+ removeViewBox: false,
78
+ removeTitle: false,
79
+ removeDesc: false,
80
+ removeUselessStrokeAndFill: false,
81
+ convertColors: {
82
+ currentColor: true,
83
+ names2hex: true,
84
+ rgb2hex: true,
85
+ shorthex: true,
86
+ // cspell:disable-line
87
+ shortname: true
88
+ }
89
+ }
90
+ }
91
+ },
92
+ "removeDimensions",
93
+ "cleanupNumericValues"
94
+ ]
95
+ };
96
+ function optimizeSvg(svgContent, config = defaultConfig) {
97
+ try {
98
+ const result = (0, import_svgo.optimize)(svgContent, config);
99
+ return result.data;
100
+ } catch (error) {
101
+ throw new Error(`Failed to optimize SVG: ${error}`);
102
+ }
103
+ }
104
+
105
+ // src/utils/name.ts
106
+ function pascalCase(str) {
107
+ 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("");
108
+ }
109
+ function formatComponentName(name, prefix, suffix) {
110
+ const prefixPart = prefix ? pascalCase(prefix) : "";
111
+ const suffixPart = suffix ? pascalCase(suffix) : "";
112
+ const baseName = pascalCase(name);
113
+ return `${prefixPart}${baseName}${suffixPart}`;
114
+ }
115
+
116
+ // src/converters/react.ts
117
+ async function convertToReact(svgContent, options = {}) {
118
+ const {
119
+ name,
120
+ prefix,
121
+ suffix,
122
+ optimize: optimize2 = true,
123
+ typescript = true,
124
+ memo = true,
125
+ ref = true,
126
+ titleProp = true,
127
+ descProp = true
128
+ } = options;
129
+ try {
130
+ let processedSvg = svgContent;
131
+ if (optimize2) {
132
+ processedSvg = optimizeSvg(svgContent);
133
+ }
134
+ const baseName = name || "icon";
135
+ const componentName = formatComponentName(baseName, prefix, suffix);
136
+ const svgrOptions = {
137
+ typescript,
138
+ memo,
139
+ ref,
140
+ titleProp,
141
+ descProp,
142
+ svgProps: {
143
+ className: "{className}"
144
+ }
145
+ };
146
+ let result = await (0, import_core.transform)(processedSvg, svgrOptions, {
147
+ componentName
148
+ });
149
+ const typeImports = typescript ? `import { SVGProps } from 'react';
150
+ ` : "";
151
+ const memoImport = memo ? `import { memo } from 'react';
152
+ ` : "";
153
+ const refImport = ref ? `import { forwardRef } from 'react';
154
+ ` : "";
155
+ const exports = `
156
+ export default ${componentName};
157
+ export { ${componentName} };`;
158
+ const propsType = typescript ? `SVGProps<SVGSVGElement> & { className?: string; }` : "";
159
+ const componentProps = typescript ? `props: ${propsType}` : "props";
160
+ const componentFunc = memo ? `memo(` : "";
161
+ const refWrapper = ref ? `forwardRef<SVGSVGElement, ${propsType}>(` : "";
162
+ const closingWrappers = `${ref ? ")" : ""}${memo ? ")" : ""}`;
163
+ result = `${typeImports}${memoImport}${refImport}
164
+ const ${componentName} = ${componentFunc}${refWrapper}(${componentProps}) => {
165
+ return ${result};
166
+ }${closingWrappers};
167
+
168
+ ${componentName}.displayName = "${componentName}";
169
+ ${exports}`;
170
+ const extension = getFileExtension("react", typescript);
171
+ const filename = getComponentFilename("icon.svg", componentName, extension);
172
+ return {
173
+ code: result,
174
+ filename,
175
+ componentName
176
+ };
177
+ } catch (error) {
178
+ throw new Error(`Failed to convert SVG to React: ${error}`);
179
+ }
180
+ }
181
+
182
+ // src/converters/vue.ts
183
+ function convertToVue(svgContent, options = {}) {
184
+ const {
185
+ name,
186
+ prefix,
187
+ suffix,
188
+ optimize: optimize2 = true,
189
+ typescript = true,
190
+ compositionApi: _compositionApi = true,
191
+ // eslint-disable-line @typescript-eslint/no-unused-vars
192
+ scriptSetup = true
193
+ } = options;
194
+ try {
195
+ let processedSvg = svgContent;
196
+ if (optimize2) {
197
+ processedSvg = optimizeSvg(svgContent);
198
+ }
199
+ const baseName = name || "icon";
200
+ const componentName = formatComponentName(baseName, prefix, suffix);
201
+ 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");
202
+ const scriptTag = scriptSetup ? generateScriptSetup(typescript, componentName) : generateCompositionScript(componentName, typescript);
203
+ const template = `<template>
204
+ ${cleanedSvg}
205
+ </template>`;
206
+ const style = `<style scoped>
30
207
  /* 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?`
208
+ </style>`;
209
+ const vueComponent = `${scriptTag}
210
+
211
+ ${template}
212
+
213
+ ${style}`;
214
+ const extension = getFileExtension("vue", typescript);
215
+ const filename = getComponentFilename("icon.svg", componentName, extension);
216
+ return {
217
+ code: vueComponent,
218
+ filename,
219
+ componentName
220
+ };
221
+ } catch (error) {
222
+ throw new Error(`Failed to convert SVG to Vue: ${error}`);
223
+ }
224
+ }
225
+ function generateScriptSetup(typescript, componentName) {
226
+ const lang = typescript ? ' lang="ts"' : "";
227
+ const propsType = typescript ? `
32
228
  interface Props {
33
229
  class?: string;
34
230
  style?: string | Record<string, any>;
35
- }`:""}${e?`
231
+ }` : "";
232
+ const propsDefinition = typescript ? `
36
233
  const props = withDefaults(defineProps<Props>(), {
37
234
  class: '',
38
235
  style: undefined,
39
- });`:`
236
+ });` : `
40
237
  const props = withDefaults(defineProps(), {
41
238
  class: '',
42
239
  style: undefined,
43
- });`}
240
+ });`;
241
+ return `<script setup${lang}>${propsType}${propsDefinition}
44
242
 
45
243
  // Component name for debugging
46
- const __name = '${t}';
47
- </script>`}function ee(e,t){let r=t?' lang="ts"':"",n=t?`
244
+ const __name = '${componentName}';
245
+ </script>`;
246
+ }
247
+ function generateCompositionScript(componentName, typescript) {
248
+ const lang = typescript ? ' lang="ts"' : "";
249
+ const propsType = typescript ? `
48
250
  interface Props {
49
251
  class?: string;
50
252
  style?: string | Record<string, any>;
51
- }`:"",s=t?`
52
- const ${e} = defineComponent({
53
- name: '${e}',
253
+ }` : "";
254
+ const exportStatement = typescript ? `
255
+ const ${componentName} = defineComponent({
256
+ name: '${componentName}',
54
257
  props: {
55
258
  class: {
56
259
  type: String,
@@ -66,10 +269,10 @@ const ${e} = defineComponent({
66
269
  },
67
270
  });
68
271
 
69
- export default ${e};
70
- export { ${e} };`:`
71
- const ${e} = defineComponent({
72
- name: '${e}',
272
+ export default ${componentName};
273
+ export { ${componentName} };` : `
274
+ const ${componentName} = defineComponent({
275
+ name: '${componentName}',
73
276
  props: {
74
277
  class: {
75
278
  type: String,
@@ -85,7 +288,59 @@ const ${e} = defineComponent({
85
288
  },
86
289
  });
87
290
 
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();
291
+ export default ${componentName};
292
+ export { ${componentName} };`;
293
+ return `<script${lang}>
294
+ import { defineComponent } from 'vue';${propsType}${exportStatement}
295
+ </script>`;
296
+ }
297
+
298
+ // src/cli.ts
299
+ var import_url = require("url");
300
+ var import_path2 = require("path");
301
+ var import_fs2 = require("fs");
302
+ var __filename2 = (0, import_url.fileURLToPath)(importMetaUrl);
303
+ var __dirname = (0, import_path2.dirname)(__filename2);
304
+ var packageJson = JSON.parse(
305
+ (0, import_fs2.readFileSync)((0, import_path2.join)(__dirname, "..", "package.json"), "utf-8")
306
+ );
307
+ var program = new import_commander.Command();
308
+ program.name("svgfusion").description(
309
+ "Transform SVG files into production-ready React and Vue 3 components"
310
+ ).version(packageJson.version);
311
+ 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(
312
+ "-f, --framework <framework>",
313
+ "Target framework (react|vue)",
314
+ "react"
315
+ ).option("-t, --typescript", "Generate TypeScript files", false).option("--no-optimize", "Skip SVG optimization").action(
316
+ async (input, options) => {
317
+ console.log("\u{1F504} Processing SVG files...");
318
+ try {
319
+ const { framework, output, typescript, optimize: optimize2 } = options;
320
+ if (framework !== "react" && framework !== "vue") {
321
+ throw new Error('Framework must be either "react" or "vue"');
322
+ }
323
+ const svgFiles = await readSvgDirectory(input);
324
+ if (svgFiles.length === 0) {
325
+ throw new Error("No SVG files found in the input path");
326
+ }
327
+ console.log(`\u{1F504} Converting ${svgFiles.length} SVG file(s)...`);
328
+ for (const filePath of svgFiles) {
329
+ const svgContent = await readSvgFile(filePath);
330
+ const optimizedSvg = optimize2 ? optimizeSvg(svgContent) : svgContent;
331
+ const result = framework === "react" ? await convertToReact(optimizedSvg, { typescript }) : convertToVue(optimizedSvg, { typescript });
332
+ const outputPath = (0, import_path2.join)(output, result.filename);
333
+ await writeComponentFile(outputPath, result.code);
334
+ }
335
+ console.log(
336
+ `\u2705 Successfully converted ${svgFiles.length} SVG file(s) to ${framework} components`
337
+ );
338
+ } catch (error) {
339
+ console.error(
340
+ `\u274C Error: ${error instanceof Error ? error.message : "Unknown error"}`
341
+ );
342
+ process.exit(1);
343
+ }
344
+ }
345
+ );
346
+ program.parse();
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "svgfusion",
3
- "version": "1.0.0-beta.3",
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.5",
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",
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();