svgfusion 1.5.0 → 1.6.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 +1 -1
- package/dist/index.d.mts +47 -1
- package/dist/index.d.ts +47 -1
- package/dist/index.js +43 -6
- package/dist/index.mjs +43 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -61,7 +61,7 @@ pnpm add svgfusion
|
|
|
61
61
|
|
|
62
62
|
## CLI Options
|
|
63
63
|
|
|
64
|
-
<img src="https://i.ibb.co/
|
|
64
|
+
<img src="https://i.ibb.co/TD0QP5FC/cli.png" alt="SVGFusion CLI" width="512" >
|
|
65
65
|
|
|
66
66
|
svgfusion ./icons --output ./components --prefix Icon --suffix Svg
|
|
67
67
|
|
package/dist/index.d.mts
CHANGED
|
@@ -159,4 +159,50 @@ declare function sanitizeComponentName(name: string): string;
|
|
|
159
159
|
*/
|
|
160
160
|
declare function formatComponentName(name: string, prefix?: string, suffix?: string): string;
|
|
161
161
|
|
|
162
|
-
|
|
162
|
+
interface IndexGenerationOptions {
|
|
163
|
+
format: 'ts' | 'js';
|
|
164
|
+
exportType: 'named' | 'default';
|
|
165
|
+
typescript: boolean;
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Generate index file content for tree-shaking
|
|
169
|
+
*/
|
|
170
|
+
declare function generateIndexFile(results: ConversionResult[], options: IndexGenerationOptions): string;
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Batch converter for processing multiple SVG files
|
|
174
|
+
*/
|
|
175
|
+
declare class BatchConverter {
|
|
176
|
+
private reactConverter;
|
|
177
|
+
private vueConverter;
|
|
178
|
+
/**
|
|
179
|
+
* Convert multiple SVG files to framework components
|
|
180
|
+
*/
|
|
181
|
+
convertBatch(options: BatchConversionOptions): Promise<BatchConversionResult>;
|
|
182
|
+
/**
|
|
183
|
+
* Get component names from batch conversion results
|
|
184
|
+
*/
|
|
185
|
+
getComponentNames(results: BatchConversionResult): string[];
|
|
186
|
+
/**
|
|
187
|
+
* Generate summary report of conversion results
|
|
188
|
+
*/
|
|
189
|
+
generateSummaryReport(results: BatchConversionResult): string;
|
|
190
|
+
/**
|
|
191
|
+
* Get all SVG files in directory
|
|
192
|
+
*/
|
|
193
|
+
private getSvgFiles;
|
|
194
|
+
/**
|
|
195
|
+
* Generate index file for tree-shaking
|
|
196
|
+
*/
|
|
197
|
+
private generateIndexFile;
|
|
198
|
+
/**
|
|
199
|
+
* Get component name from file path
|
|
200
|
+
*/
|
|
201
|
+
private getComponentNameFromFile;
|
|
202
|
+
/**
|
|
203
|
+
* Get output path for converted component
|
|
204
|
+
*/
|
|
205
|
+
private getOutputPath;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
export { type BatchConversionOptions, type BatchConversionResult, BatchConverter, type CliOptions, type ConversionError, type ConversionOptions, type ConversionResult, type Framework, type IndexGenerationOptions, type ReactConversionOptions, type VueConversionOptions, convertToReact, convertToVue, createSvgoConfig, formatComponentName, generateIndexFile, optimizeSvg, pascalCase, readSvgDirectory, readSvgFile, sanitizeComponentName, svgToComponentName, writeComponentFile, writeSvgFile };
|
package/dist/index.d.ts
CHANGED
|
@@ -159,4 +159,50 @@ declare function sanitizeComponentName(name: string): string;
|
|
|
159
159
|
*/
|
|
160
160
|
declare function formatComponentName(name: string, prefix?: string, suffix?: string): string;
|
|
161
161
|
|
|
162
|
-
|
|
162
|
+
interface IndexGenerationOptions {
|
|
163
|
+
format: 'ts' | 'js';
|
|
164
|
+
exportType: 'named' | 'default';
|
|
165
|
+
typescript: boolean;
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Generate index file content for tree-shaking
|
|
169
|
+
*/
|
|
170
|
+
declare function generateIndexFile(results: ConversionResult[], options: IndexGenerationOptions): string;
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Batch converter for processing multiple SVG files
|
|
174
|
+
*/
|
|
175
|
+
declare class BatchConverter {
|
|
176
|
+
private reactConverter;
|
|
177
|
+
private vueConverter;
|
|
178
|
+
/**
|
|
179
|
+
* Convert multiple SVG files to framework components
|
|
180
|
+
*/
|
|
181
|
+
convertBatch(options: BatchConversionOptions): Promise<BatchConversionResult>;
|
|
182
|
+
/**
|
|
183
|
+
* Get component names from batch conversion results
|
|
184
|
+
*/
|
|
185
|
+
getComponentNames(results: BatchConversionResult): string[];
|
|
186
|
+
/**
|
|
187
|
+
* Generate summary report of conversion results
|
|
188
|
+
*/
|
|
189
|
+
generateSummaryReport(results: BatchConversionResult): string;
|
|
190
|
+
/**
|
|
191
|
+
* Get all SVG files in directory
|
|
192
|
+
*/
|
|
193
|
+
private getSvgFiles;
|
|
194
|
+
/**
|
|
195
|
+
* Generate index file for tree-shaking
|
|
196
|
+
*/
|
|
197
|
+
private generateIndexFile;
|
|
198
|
+
/**
|
|
199
|
+
* Get component name from file path
|
|
200
|
+
*/
|
|
201
|
+
private getComponentNameFromFile;
|
|
202
|
+
/**
|
|
203
|
+
* Get output path for converted component
|
|
204
|
+
*/
|
|
205
|
+
private getOutputPath;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
export { type BatchConversionOptions, type BatchConversionResult, BatchConverter, type CliOptions, type ConversionError, type ConversionOptions, type ConversionResult, type Framework, type IndexGenerationOptions, type ReactConversionOptions, type VueConversionOptions, convertToReact, convertToVue, createSvgoConfig, formatComponentName, generateIndexFile, optimizeSvg, pascalCase, readSvgDirectory, readSvgFile, sanitizeComponentName, svgToComponentName, writeComponentFile, writeSvgFile };
|
package/dist/index.js
CHANGED
|
@@ -1,20 +1,57 @@
|
|
|
1
|
-
'use strict';var promises=require('fs/promises'),path=require('path'),fs=require('fs'),core=require('@svgr/core'),svgo=require('svgo');var
|
|
1
|
+
'use strict';var promises=require('fs/promises'),path=require('path'),fs=require('fs'),core=require('@svgr/core'),svgo=require('svgo');var V=Object.defineProperty;var X=Object.getOwnPropertyDescriptor;var Y=Object.getOwnPropertyNames;var ee=Object.prototype.hasOwnProperty;var O=(e,t)=>()=>(e&&(t=e(e=0)),t);var z=(e,t)=>{for(var r in t)V(e,r,{get:t[r],enumerable:true});},te=(e,t,r,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of Y(t))!ee.call(e,o)&&o!==r&&V(e,o,{get:()=>t[o],enumerable:!(n=X(t,o))||n.enumerable});return e};var re=e=>te(V({},"__esModule",{value:true}),e);var c=O(()=>{});var U={};z(U,{pascalCase:()=>exports.pascalCase});exports.pascalCase=void 0;var E=O(()=>{c();exports.pascalCase=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(t=>t.charAt(0).toUpperCase()+t.slice(1).toLowerCase()).join("")||"";});var T={};z(T,{ensureDir:()=>R,ensureDirectoryExists:()=>S,getComponentFilename:()=>Ce,getFileExtension:()=>de,readSvgDirectory:()=>D,readSvgFile:()=>q,writeComponentFile:()=>h,writeSvgFile:()=>Z});async function q(e){try{return await promises.readFile(e,"utf-8")}catch(t){throw new Error(`Failed to read SVG file: ${e}. ${t}`)}}async function Z(e,t){try{await S(path.dirname(e)),await promises.writeFile(e,t,"utf-8");}catch(r){throw new Error(`Failed to write SVG file: ${e}. ${r}`)}}async function h(e,t){try{await S(path.dirname(e)),await promises.writeFile(e,t,"utf-8");}catch(r){throw new Error(`Failed to write component file: ${e}. ${r}`)}}async function D(e,t=false){try{let r=await promises.readdir(e),n=[];for(let o of r){let s=path.join(e,o),i=await promises.stat(s);if(i.isDirectory()&&t){let a=await D(s,t);n.push(...a);}else i.isFile()&&path.extname(o).toLowerCase()===".svg"&&n.push(s);}return n}catch(r){throw new Error(`Failed to read directory: ${e}. ${r}`)}}async function S(e){fs.existsSync(e)||await promises.mkdir(e,{recursive:true});}function de(e,t=true){return e==="react"?t?".tsx":".jsx":".vue"}function Ce(e,t,r){return `${t}${r}`}var R,w=O(()=>{c();R=S;});c();c();c();c();var oe={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 I(e,t=oe){try{return svgo.optimize(e,t).data}catch(r){throw new Error(`Failed to optimize SVG: ${r}`)}}function se(e){let t=[{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&&t.push("removeDimensions"),{plugins:t}}c();E();function ie(e){let t=e.replace(/\.svg$/i,"");return t=ae(t),exports.pascalCase(t)}function ae(e){let t=e.toLowerCase().replace(/[^a-zA-Z0-9]/g," ");return t=t.replace(/\s+/g," ").trim(),t}function ce(e){return exports.pascalCase(e.replace(/[^a-zA-Z0-9]/g," "))}function x(e,t,r){let n=t?exports.pascalCase(t):"",o=r?exports.pascalCase(r):"",s=exports.pascalCase(e);return `${n}${s}${o}`}var C=class{async processSvg(t,r){let{optimize:n=true}=r;return n?I(t):t}generateComponentName(t){let{name:r,prefix:n,suffix:o}=t;return x(r||"Icon",n,o)}generateFilename(t,r,n=true){try{let{getFileExtension:o,getComponentFilename:s}=(w(),re(T)),i=o(r,n);return s("icon.svg",t,i)}catch{return `${t}${{react:n?".tsx":".jsx",vue:".vue"}[r]}`}}};c();function M(e){let{typescript:t=true,memo:r=true,ref:n=true,titleProp:o=true,descProp:s=true,icon:i=true,dimensions:a=false,replaceAttrValues:l={"#000":"currentColor","#000000":"currentColor"},svgProps:p={},expandProps:v=false,nativeProps:f=true,ariaLabelledBy:d=false,ariaHidden:u=false,role:b="img"}=e,F={typescript:t,memo:r,ref:n,titleProp:o,descProp:s,icon:i,dimensions:a,expandProps:v,svgProps:{className:"{className}",...n&&{ref:"{ref}"},...f&&{width:"{width}",height:"{height}",style:"{style}"},...d&&o&&s&&{"aria-labelledby":"{titleId} {descId}"},...u&&{"aria-hidden":"true"},role:b,...p},replaceAttrValues:l,plugins:["@svgr/plugin-svgo","@svgr/plugin-jsx","@svgr/plugin-prettier"]};return F.svgoConfig={plugins:[{name:"preset-default",params:{overrides:{removeViewBox:false,removeTitle:!o,removeDesc:!s,removeUselessStrokeAndFill:false,removeUnusedNS:false,removeUselessDefs:false,convertShapeToPath:false,mergePaths:false,convertColors:false}}},...i&&!a?[{name:"removeAttrs",params:{attrs:["width","height"]}}]:[],...i?["cleanupNumericValues"]:[]]},F}function W(e,t,r){let n=e;return B(n)&&(n=A(n,t)),n}function B(e){return ["linearGradient","radialGradient","pattern","mask","filter","clipPath","marker","symbol","use"].some(r=>e.includes(`<${r}`)||e.includes(`</${r}`))}function A(e,t){let r=`${t.toLowerCase()}_`;return e=e.replace(/id="([^"]+)"/g,`id="${r}$1"`),e=e.replace(/url\(#([^)]+)\)/g,`url(#${r}$1)`),e=e.replace(/href="#([^"]+)"/g,`href="#${r}$1"`),e}var y=class extends C{async convert(t,r={}){try{let n=this.generateComponentName(r),o=await this.processSvg(t,r);B(o)&&(o=A(o,n));let s=M(r),i=await core.transform(o,s,{componentName:n}),a=W(i,n,r),l=this.generateFilename(n,"react",r.typescript??!0);return {code:a,filename:l,componentName:n}}catch(n){throw new Error(`Failed to convert SVG to React: ${n}`)}}};async function he(e,t={}){return new y().convert(e,t)}c();c();function H(e,t){let{name:r,prefix:n,suffix:o,props:s=true,replaceAttrValues:i={"#000":"currentColor","#000000":"currentColor"}}=t,l=x(r||"Icon",n,o),p=we(e);return p=ye(p,i),$e(p)&&(p=Fe(p,l)),p=Se(p,s),{code:Re(p,l,t),componentName:l}}function we(e){return e.replace(/<\?xml[^>]*\?>\s*/,"").replace(/<!--[\s\S]*?-->/g,"").replace(/xmlns="[^"]*"/g,"").trim()}function ye(e,t){let r=e;for(let[n,o]of Object.entries(t)){let s=new RegExp(n.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),"g");r=r.replace(s,o);}return r}function $e(e){return ["linearGradient","radialGradient","pattern","mask","filter","clipPath","marker","symbol","use"].some(r=>e.includes(`<${r}`)||e.includes(`</${r}`))}function Fe(e,t){let r=`${t.toLowerCase()}_`;return e=e.replace(/id="([^"]+)"/g,`id="${r}$1"`),e=e.replace(/url\(#([^)]+)\)/g,`url(#${r}$1)`),e=e.replace(/href="#([^"]+)"/g,`href="#${r}$1"`),e}function Se(e,t){return t?e.replace("<svg",'<svg :class="className" :style="style" v-bind="$attrs"'):e}function Re(e,t,r){let{typescript:n,compositionApi:o,props:s}=r,l=`<script${n?' lang="ts"':""}${o?" setup":""}>`;o?s&&(l+=`
|
|
2
2
|
interface Props {
|
|
3
3
|
className?: string;
|
|
4
4
|
style?: Record<string, any>;
|
|
5
5
|
}
|
|
6
6
|
|
|
7
7
|
defineProps<Props>();
|
|
8
|
-
`):
|
|
8
|
+
`):l+=`
|
|
9
9
|
export default {
|
|
10
|
-
name: '${
|
|
10
|
+
name: '${t}',
|
|
11
11
|
props: {
|
|
12
12
|
className: String,
|
|
13
13
|
style: Object,
|
|
14
14
|
},
|
|
15
15
|
};
|
|
16
|
-
`,
|
|
16
|
+
`,l+="</script>";let p=`
|
|
17
17
|
<template>
|
|
18
18
|
${e}
|
|
19
|
-
</template>`;return [
|
|
20
|
-
`)}var
|
|
19
|
+
</template>`;return [l,p].filter(Boolean).join(`
|
|
20
|
+
`)}var $=class extends C{async convert(t,r={}){try{let n=await this.processSvg(t,r),{code:o,componentName:s}=H(n,r),i=this.generateFilename(s,"vue",r.typescript??!0);return {code:o,filename:i,componentName:s}}catch(n){throw new Error(`Failed to convert SVG to Vue: ${n}`)}}};async function Ne(e,t={}){return new $().convert(e,t)}w();c();function k(e,t){let{format:r,exportType:n,typescript:o}=t,s=[...e].sort((i,a)=>i.componentName.localeCompare(a.componentName));return n==="default"?Pe(s):be(s,r,o)}function be(e,t,r){let n="";n+=`// Auto-generated index file for tree-shaking
|
|
21
|
+
`,n+=`// This file exports all components for optimal bundling
|
|
22
|
+
|
|
23
|
+
`;for(let o of e){let s=G(o.filename);n+=`export { default as ${o.componentName} } from './${s}';
|
|
24
|
+
`;}n+=`
|
|
25
|
+
// Barrel export for convenience
|
|
26
|
+
`,n+=`export {
|
|
27
|
+
`;for(let o of e)n+=` ${o.componentName},
|
|
28
|
+
`;if(n+=`};
|
|
29
|
+
`,r&&t==="ts"){n+=`
|
|
30
|
+
// TypeScript component types
|
|
31
|
+
`,n+=`export type IconComponent = React.ComponentType<React.SVGProps<SVGSVGElement>>;
|
|
32
|
+
`,n+=`export type IconComponents = {
|
|
33
|
+
`;for(let o of e)n+=` ${o.componentName}: IconComponent;
|
|
34
|
+
`;n+=`};
|
|
35
|
+
`;}return n}function Pe(e,t,r){let n="";n+=`// Auto-generated index file
|
|
36
|
+
`,n+=`// Warning: Default exports are less tree-shakeable
|
|
37
|
+
|
|
38
|
+
`;for(let o of e){let s=G(o.filename);n+=`import ${o.componentName} from './${s}';
|
|
39
|
+
`;}n+=`
|
|
40
|
+
export default {
|
|
41
|
+
`;for(let o of e)n+=` ${o.componentName},
|
|
42
|
+
`;n+=`};
|
|
43
|
+
`,n+=`
|
|
44
|
+
// Individual exports for flexibility
|
|
45
|
+
`;for(let o of e)n+=`export { default as ${o.componentName} } from './${G(o.filename)}';
|
|
46
|
+
`;return n}function G(e){return e.replace(/\.(tsx?|jsx?|vue)$/,"")}c();w();var j=class{reactConverter=new y;vueConverter=new $;async convertBatch(t){let{inputDir:r,outputDir:n,recursive:o=false,extensions:s=[".svg"],generateIndex:i=false,framework:a="react",...l}=t,p=[],v=[];try{await R(n);let f=await this.getSvgFiles(r,o,s);for(let d of f)try{let{readSvgFile:u}=await Promise.resolve().then(()=>(w(),T)),b=await u(d),F=a==="react"?this.reactConverter:this.vueConverter,K=await this.getComponentNameFromFile(d,t),P=await F.convert(b,{...l,name:K}),Q=await this.getOutputPath(d,r,n,P.filename);await h(Q,P.code),p.push(P);}catch(u){v.push({file:d,error:u instanceof Error?u.message:String(u),stack:u instanceof Error?u.stack:void 0});}return i&&p.length>0&&await this.generateIndexFile(n,p,t),{results:p,errors:v,summary:{total:f.length,successful:p.length,failed:v.length}}}catch(f){throw new Error(`Batch conversion failed: ${f instanceof Error?f.message:String(f)}`)}}getComponentNames(t){return t.results.map(r=>r.componentName)}generateSummaryReport(t){let{summary:r,errors:n}=t,o=this.getComponentNames(t),s=`
|
|
47
|
+
=== SVG Conversion Summary ===
|
|
48
|
+
`;return s+=`Total files processed: ${r.total}
|
|
49
|
+
`,s+=`Successful conversions: ${r.successful}
|
|
50
|
+
`,s+=`Failed conversions: ${r.failed}
|
|
51
|
+
`,o.length>0&&(s+=`
|
|
52
|
+
Generated components:
|
|
53
|
+
`,o.forEach(i=>{s+=` - ${i}
|
|
54
|
+
`;})),n.length>0&&(s+=`
|
|
55
|
+
Errors:
|
|
56
|
+
`,n.forEach(i=>{s+=` - ${i.file}: ${i.error}
|
|
57
|
+
`;})),s}async getSvgFiles(t,r,n){let o=[],s=await promises.readdir(t);for(let i of s){let a=path.join(t,i),l=await promises.stat(a);if(l.isDirectory()&&r){let p=await this.getSvgFiles(a,r,n);o.push(...p);}else l.isFile()&&n.includes(path.extname(i).toLowerCase())&&o.push(a);}return o}async generateIndexFile(t,r,n){let{indexFormat:o="ts",exportType:s="named",typescript:i=true}=n,a=k(r,{format:o,exportType:s,typescript:i}),l=`index.${o}`,p=path.join(t,l);await h(p,a);}async getComponentNameFromFile(t,r){let n=path.basename(t,path.extname(t)),{prefix:o="",suffix:s=""}=r,{pascalCase:i}=await Promise.resolve().then(()=>(E(),U)),a=i(n);return o&&(a=`${i(o)}${a}`),s&&(a=`${a}${i(s)}`),a}async getOutputPath(t,r,n,o){let s=t.replace(r,"").replace(/^[/\\]/,""),i=s.includes("/")?s.substring(0,s.lastIndexOf("/")):s.includes("\\")?s.substring(0,s.lastIndexOf("\\")):"",a=i?path.join(n,i,o):path.join(n,o);return await R(path.dirname(a)),a}};exports.BatchConverter=j;exports.convertToReact=he;exports.convertToVue=Ne;exports.createSvgoConfig=se;exports.formatComponentName=x;exports.generateIndexFile=k;exports.optimizeSvg=I;exports.readSvgDirectory=D;exports.readSvgFile=q;exports.sanitizeComponentName=ce;exports.svgToComponentName=ie;exports.writeComponentFile=h;exports.writeSvgFile=Z;
|
package/dist/index.mjs
CHANGED
|
@@ -1,20 +1,57 @@
|
|
|
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
|
|
1
|
+
import {dirname,join,extname,basename}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 O=Object.defineProperty;var Y=Object.getOwnPropertyDescriptor;var ee=Object.getOwnPropertyNames;var te=Object.prototype.hasOwnProperty;var E=(e,t)=>()=>(e&&(t=e(e=0)),t);var U=(e,t)=>{for(var r in t)O(e,r,{get:t[r],enumerable:true});},re=(e,t,r,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of ee(t))!te.call(e,o)&&o!==r&&O(e,o,{get:()=>t[o],enumerable:!(n=Y(t,o))||n.enumerable});return e};var ne=e=>re(O({},"__esModule",{value:true}),e);var c=E(()=>{});var L={};U(L,{pascalCase:()=>v});var v,D=E(()=>{c();v=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(t=>t.charAt(0).toUpperCase()+t.slice(1).toLowerCase()).join("")||"";});var B={};U(B,{ensureDir:()=>R,ensureDirectoryExists:()=>N,getComponentFilename:()=>xe,getFileExtension:()=>Ce,readSvgDirectory:()=>T,readSvgFile:()=>Z,writeComponentFile:()=>w,writeSvgFile:()=>W});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 W(e,t){try{await N(dirname(e)),await writeFile(e,t,"utf-8");}catch(r){throw new Error(`Failed to write SVG file: ${e}. ${r}`)}}async function w(e,t){try{await N(dirname(e)),await writeFile(e,t,"utf-8");}catch(r){throw new Error(`Failed to write component file: ${e}. ${r}`)}}async function T(e,t=false){try{let r=await readdir(e),n=[];for(let o of r){let s=join(e,o),i=await stat(s);if(i.isDirectory()&&t){let a=await T(s,t);n.push(...a);}else i.isFile()&&extname(o).toLowerCase()===".svg"&&n.push(s);}return n}catch(r){throw new Error(`Failed to read directory: ${e}. ${r}`)}}async function N(e){existsSync(e)||await mkdir(e,{recursive:true});}function Ce(e,t=true){return e==="react"?t?".tsx":".jsx":".vue"}function xe(e,t,r){return `${t}${r}`}var R,y=E(()=>{c();R=N;});c();c();c();c();var se={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 I(e,t=se){try{return optimize(e,t).data}catch(r){throw new Error(`Failed to optimize SVG: ${r}`)}}function ie(e){let t=[{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&&t.push("removeDimensions"),{plugins:t}}c();D();function ae(e){let t=e.replace(/\.svg$/i,"");return t=ce(t),v(t)}function ce(e){let t=e.toLowerCase().replace(/[^a-zA-Z0-9]/g," ");return t=t.replace(/\s+/g," ").trim(),t}function pe(e){return v(e.replace(/[^a-zA-Z0-9]/g," "))}function h(e,t,r){let n=t?v(t):"",o=r?v(r):"",s=v(e);return `${n}${s}${o}`}var x=class{async processSvg(t,r){let{optimize:n=true}=r;return n?I(t):t}generateComponentName(t){let{name:r,prefix:n,suffix:o}=t;return h(r||"Icon",n,o)}generateFilename(t,r,n=true){try{let{getFileExtension:o,getComponentFilename:s}=(y(),ne(B)),i=o(r,n);return s("icon.svg",t,i)}catch{return `${t}${{react:n?".tsx":".jsx",vue:".vue"}[r]}`}}};c();function H(e){let{typescript:t=true,memo:r=true,ref:n=true,titleProp:o=true,descProp:s=true,icon:i=true,dimensions:a=false,replaceAttrValues:m={"#000":"currentColor","#000000":"currentColor"},svgProps:p={},expandProps:d=false,nativeProps:g=true,ariaLabelledBy:C=false,ariaHidden:f=false,role:P="img"}=e,S={typescript:t,memo:r,ref:n,titleProp:o,descProp:s,icon:i,dimensions:a,expandProps:d,svgProps:{className:"{className}",...n&&{ref:"{ref}"},...g&&{width:"{width}",height:"{height}",style:"{style}"},...C&&o&&s&&{"aria-labelledby":"{titleId} {descId}"},...f&&{"aria-hidden":"true"},role:P,...p},replaceAttrValues:m,plugins:["@svgr/plugin-svgo","@svgr/plugin-jsx","@svgr/plugin-prettier"]};return S.svgoConfig={plugins:[{name:"preset-default",params:{overrides:{removeViewBox:false,removeTitle:!o,removeDesc:!s,removeUselessStrokeAndFill:false,removeUnusedNS:false,removeUselessDefs:false,convertShapeToPath:false,mergePaths:false,convertColors:false}}},...i&&!a?[{name:"removeAttrs",params:{attrs:["width","height"]}}]:[],...i?["cleanupNumericValues"]:[]]},S}function J(e,t,r){let n=e;return A(n)&&(n=G(n,t)),n}function A(e){return ["linearGradient","radialGradient","pattern","mask","filter","clipPath","marker","symbol","use"].some(r=>e.includes(`<${r}`)||e.includes(`</${r}`))}function G(e,t){let r=`${t.toLowerCase()}_`;return e=e.replace(/id="([^"]+)"/g,`id="${r}$1"`),e=e.replace(/url\(#([^)]+)\)/g,`url(#${r}$1)`),e=e.replace(/href="#([^"]+)"/g,`href="#${r}$1"`),e}var $=class extends x{async convert(t,r={}){try{let n=this.generateComponentName(r),o=await this.processSvg(t,r);A(o)&&(o=G(o,n));let s=H(r),i=await transform(o,s,{componentName:n}),a=J(i,n,r),m=this.generateFilename(n,"react",r.typescript??!0);return {code:a,filename:m,componentName:n}}catch(n){throw new Error(`Failed to convert SVG to React: ${n}`)}}};async function we(e,t={}){return new $().convert(e,t)}c();c();function K(e,t){let{name:r,prefix:n,suffix:o,props:s=true,replaceAttrValues:i={"#000":"currentColor","#000000":"currentColor"}}=t,m=h(r||"Icon",n,o),p=ye(e);return p=$e(p,i),Fe(p)&&(p=Se(p,m)),p=Ne(p,s),{code:Re(p,m,t),componentName:m}}function ye(e){return e.replace(/<\?xml[^>]*\?>\s*/,"").replace(/<!--[\s\S]*?-->/g,"").replace(/xmlns="[^"]*"/g,"").trim()}function $e(e,t){let r=e;for(let[n,o]of Object.entries(t)){let s=new RegExp(n.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),"g");r=r.replace(s,o);}return r}function Fe(e){return ["linearGradient","radialGradient","pattern","mask","filter","clipPath","marker","symbol","use"].some(r=>e.includes(`<${r}`)||e.includes(`</${r}`))}function Se(e,t){let r=`${t.toLowerCase()}_`;return e=e.replace(/id="([^"]+)"/g,`id="${r}$1"`),e=e.replace(/url\(#([^)]+)\)/g,`url(#${r}$1)`),e=e.replace(/href="#([^"]+)"/g,`href="#${r}$1"`),e}function Ne(e,t){return t?e.replace("<svg",'<svg :class="className" :style="style" v-bind="$attrs"'):e}function Re(e,t,r){let{typescript:n,compositionApi:o,props:s}=r,m=`<script${n?' lang="ts"':""}${o?" setup":""}>`;o?s&&(m+=`
|
|
2
2
|
interface Props {
|
|
3
3
|
className?: string;
|
|
4
4
|
style?: Record<string, any>;
|
|
5
5
|
}
|
|
6
6
|
|
|
7
7
|
defineProps<Props>();
|
|
8
|
-
`):
|
|
8
|
+
`):m+=`
|
|
9
9
|
export default {
|
|
10
|
-
name: '${
|
|
10
|
+
name: '${t}',
|
|
11
11
|
props: {
|
|
12
12
|
className: String,
|
|
13
13
|
style: Object,
|
|
14
14
|
},
|
|
15
15
|
};
|
|
16
|
-
`,
|
|
16
|
+
`,m+="</script>";let p=`
|
|
17
17
|
<template>
|
|
18
18
|
${e}
|
|
19
|
-
</template>`;return [
|
|
20
|
-
`)}var
|
|
19
|
+
</template>`;return [m,p].filter(Boolean).join(`
|
|
20
|
+
`)}var F=class extends x{async convert(t,r={}){try{let n=await this.processSvg(t,r),{code:o,componentName:s}=K(n,r),i=this.generateFilename(s,"vue",r.typescript??!0);return {code:o,filename:i,componentName:s}}catch(n){throw new Error(`Failed to convert SVG to Vue: ${n}`)}}};async function be(e,t={}){return new F().convert(e,t)}y();c();function z(e,t){let{format:r,exportType:n,typescript:o}=t,s=[...e].sort((i,a)=>i.componentName.localeCompare(a.componentName));return n==="default"?Ve(s):Pe(s,r,o)}function Pe(e,t,r){let n="";n+=`// Auto-generated index file for tree-shaking
|
|
21
|
+
`,n+=`// This file exports all components for optimal bundling
|
|
22
|
+
|
|
23
|
+
`;for(let o of e){let s=k(o.filename);n+=`export { default as ${o.componentName} } from './${s}';
|
|
24
|
+
`;}n+=`
|
|
25
|
+
// Barrel export for convenience
|
|
26
|
+
`,n+=`export {
|
|
27
|
+
`;for(let o of e)n+=` ${o.componentName},
|
|
28
|
+
`;if(n+=`};
|
|
29
|
+
`,r&&t==="ts"){n+=`
|
|
30
|
+
// TypeScript component types
|
|
31
|
+
`,n+=`export type IconComponent = React.ComponentType<React.SVGProps<SVGSVGElement>>;
|
|
32
|
+
`,n+=`export type IconComponents = {
|
|
33
|
+
`;for(let o of e)n+=` ${o.componentName}: IconComponent;
|
|
34
|
+
`;n+=`};
|
|
35
|
+
`;}return n}function Ve(e,t,r){let n="";n+=`// Auto-generated index file
|
|
36
|
+
`,n+=`// Warning: Default exports are less tree-shakeable
|
|
37
|
+
|
|
38
|
+
`;for(let o of e){let s=k(o.filename);n+=`import ${o.componentName} from './${s}';
|
|
39
|
+
`;}n+=`
|
|
40
|
+
export default {
|
|
41
|
+
`;for(let o of e)n+=` ${o.componentName},
|
|
42
|
+
`;n+=`};
|
|
43
|
+
`,n+=`
|
|
44
|
+
// Individual exports for flexibility
|
|
45
|
+
`;for(let o of e)n+=`export { default as ${o.componentName} } from './${k(o.filename)}';
|
|
46
|
+
`;return n}function k(e){return e.replace(/\.(tsx?|jsx?|vue)$/,"")}c();y();var j=class{reactConverter=new $;vueConverter=new F;async convertBatch(t){let{inputDir:r,outputDir:n,recursive:o=false,extensions:s=[".svg"],generateIndex:i=false,framework:a="react",...m}=t,p=[],d=[];try{await R(n);let g=await this.getSvgFiles(r,o,s);for(let C of g)try{let{readSvgFile:f}=await Promise.resolve().then(()=>(y(),B)),P=await f(C),S=a==="react"?this.reactConverter:this.vueConverter,Q=await this.getComponentNameFromFile(C,t),V=await S.convert(P,{...m,name:Q}),X=await this.getOutputPath(C,r,n,V.filename);await w(X,V.code),p.push(V);}catch(f){d.push({file:C,error:f instanceof Error?f.message:String(f),stack:f instanceof Error?f.stack:void 0});}return i&&p.length>0&&await this.generateIndexFile(n,p,t),{results:p,errors:d,summary:{total:g.length,successful:p.length,failed:d.length}}}catch(g){throw new Error(`Batch conversion failed: ${g instanceof Error?g.message:String(g)}`)}}getComponentNames(t){return t.results.map(r=>r.componentName)}generateSummaryReport(t){let{summary:r,errors:n}=t,o=this.getComponentNames(t),s=`
|
|
47
|
+
=== SVG Conversion Summary ===
|
|
48
|
+
`;return s+=`Total files processed: ${r.total}
|
|
49
|
+
`,s+=`Successful conversions: ${r.successful}
|
|
50
|
+
`,s+=`Failed conversions: ${r.failed}
|
|
51
|
+
`,o.length>0&&(s+=`
|
|
52
|
+
Generated components:
|
|
53
|
+
`,o.forEach(i=>{s+=` - ${i}
|
|
54
|
+
`;})),n.length>0&&(s+=`
|
|
55
|
+
Errors:
|
|
56
|
+
`,n.forEach(i=>{s+=` - ${i.file}: ${i.error}
|
|
57
|
+
`;})),s}async getSvgFiles(t,r,n){let o=[],s=await readdir(t);for(let i of s){let a=join(t,i),m=await stat(a);if(m.isDirectory()&&r){let p=await this.getSvgFiles(a,r,n);o.push(...p);}else m.isFile()&&n.includes(extname(i).toLowerCase())&&o.push(a);}return o}async generateIndexFile(t,r,n){let{indexFormat:o="ts",exportType:s="named",typescript:i=true}=n,a=z(r,{format:o,exportType:s,typescript:i}),m=`index.${o}`,p=join(t,m);await w(p,a);}async getComponentNameFromFile(t,r){let n=basename(t,extname(t)),{prefix:o="",suffix:s=""}=r,{pascalCase:i}=await Promise.resolve().then(()=>(D(),L)),a=i(n);return o&&(a=`${i(o)}${a}`),s&&(a=`${a}${i(s)}`),a}async getOutputPath(t,r,n,o){let s=t.replace(r,"").replace(/^[/\\]/,""),i=s.includes("/")?s.substring(0,s.lastIndexOf("/")):s.includes("\\")?s.substring(0,s.lastIndexOf("\\")):"",a=i?join(n,i,o):join(n,o);return await R(dirname(a)),a}};export{j as BatchConverter,we as convertToReact,be as convertToVue,ie as createSvgoConfig,h as formatComponentName,z as generateIndexFile,I as optimizeSvg,v as pascalCase,T as readSvgDirectory,Z as readSvgFile,pe as sanitizeComponentName,ae as svgToComponentName,w as writeComponentFile,W as writeSvgFile};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "svgfusion",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.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",
|