svger-cli 2.0.3 → 2.0.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/dist/config.js CHANGED
@@ -35,17 +35,100 @@ export async function initConfig() {
35
35
  return;
36
36
  }
37
37
  const defaultConfig = {
38
+ // Source & Output
38
39
  source: './src/assets/svg',
39
40
  output: './src/components/icons',
41
+ // Framework Configuration
42
+ framework: 'react',
43
+ typescript: true,
44
+ componentType: 'functional',
45
+ // Processing Options
40
46
  watch: false,
47
+ parallel: true,
48
+ batchSize: 10,
49
+ maxConcurrency: 4,
50
+ cache: true,
51
+ // Default Properties
41
52
  defaultWidth: 24,
42
53
  defaultHeight: 24,
43
54
  defaultFill: 'currentColor',
44
- exclude: [],
55
+ defaultStroke: 'none',
56
+ defaultStrokeWidth: 1,
57
+ // Styling Configuration
45
58
  styleRules: {
46
59
  fill: 'inherit',
47
60
  stroke: 'none',
48
61
  },
62
+ responsive: {
63
+ breakpoints: ['sm', 'md', 'lg', 'xl'],
64
+ values: {
65
+ width: ['16px', '20px', '24px', '32px'],
66
+ height: ['16px', '20px', '24px', '32px'],
67
+ },
68
+ },
69
+ theme: {
70
+ mode: 'auto',
71
+ variables: {
72
+ primary: 'currentColor',
73
+ secondary: '#6b7280',
74
+ accent: '#3b82f6',
75
+ },
76
+ },
77
+ animations: [],
78
+ // Advanced Options
79
+ plugins: [],
80
+ exclude: [],
81
+ include: [],
82
+ // Error Handling
83
+ errorHandling: {
84
+ strategy: 'continue',
85
+ maxRetries: 3,
86
+ timeout: 30000,
87
+ },
88
+ // Performance Settings
89
+ performance: {
90
+ optimization: 'balanced',
91
+ memoryLimit: 512,
92
+ cacheTimeout: 3600000,
93
+ },
94
+ // Output Customization
95
+ outputConfig: {
96
+ naming: 'pascal',
97
+ extension: 'tsx',
98
+ directory: './src/components/icons',
99
+ },
100
+ // Framework-specific configurations
101
+ react: {
102
+ componentType: 'functional',
103
+ forwardRef: true,
104
+ memo: false,
105
+ propsInterface: 'SVGProps',
106
+ styledComponents: false,
107
+ cssModules: false,
108
+ },
109
+ vue: {
110
+ api: 'composition',
111
+ setup: true,
112
+ typescript: true,
113
+ scoped: true,
114
+ cssVariables: true,
115
+ },
116
+ angular: {
117
+ standalone: true,
118
+ signals: true,
119
+ changeDetection: 'OnPush',
120
+ encapsulation: 'Emulated',
121
+ },
122
+ // Legacy support (deprecated)
123
+ template: {
124
+ type: 'default',
125
+ },
126
+ frameworkOptions: {
127
+ forwardRef: true,
128
+ memo: false,
129
+ scriptSetup: true,
130
+ standalone: true,
131
+ },
49
132
  };
50
133
  writeConfig(defaultConfig);
51
134
  console.log('✅ Config file created:', getConfigPath());
@@ -71,12 +71,13 @@ export class SVGProcessor {
71
71
  case 'kebab':
72
72
  // Keep kebab-case for filename, but component still needs PascalCase
73
73
  return toPascalCase(baseName);
74
- case 'camel':
74
+ case 'camel': {
75
75
  // Convert to camelCase
76
76
  const pascalName = toPascalCase(baseName);
77
77
  return pascalName.charAt(0).toLowerCase() + pascalName.slice(1);
78
+ }
78
79
  case 'pascal':
79
- default:
80
+ default: {
80
81
  // Default to PascalCase
81
82
  const componentName = toPascalCase(baseName);
82
83
  // Ensure component name starts with uppercase letter
@@ -84,6 +85,7 @@ export class SVGProcessor {
84
85
  return `Svg${componentName}`;
85
86
  }
86
87
  return componentName;
88
+ }
87
89
  }
88
90
  }
89
91
  /**
@@ -190,8 +192,9 @@ export class SVGProcessor {
190
192
  try {
191
193
  // Read SVG content
192
194
  const svgContent = await FileSystem.readFile(svgFilePath, 'utf-8');
193
- // Generate component name (always PascalCase for component)
194
- const componentName = this.generateComponentName(path.basename(svgFilePath));
195
+ // Generate component name using the specified naming convention
196
+ const namingConvention = options.namingConvention || 'pascal';
197
+ const componentName = this.generateComponentName(path.basename(svgFilePath), namingConvention);
195
198
  // Generate component code
196
199
  const componentCode = await this.generateComponent(componentName, svgContent, options);
197
200
  // Ensure output directory exists
@@ -200,8 +203,7 @@ export class SVGProcessor {
200
203
  const framework = options.framework || 'react';
201
204
  const typescript = options.typescript !== undefined ? options.typescript : true;
202
205
  const fileExtension = frameworkTemplateEngine.getFileExtension(framework, typescript);
203
- // Generate filename using naming convention
204
- const namingConvention = options.namingConvention;
206
+ // Generate filename using naming convention from options
205
207
  const fileName = this.generateFileName(componentName, fileExtension, namingConvention);
206
208
  // Write component file
207
209
  const outputFilePath = path.join(outputDir, fileName);
@@ -20,39 +20,89 @@ export class ConfigService {
20
20
  */
21
21
  getDefaultConfig() {
22
22
  return {
23
+ // Source & Output
23
24
  source: './src/assets/svg',
24
25
  output: './src/components/icons',
26
+ // Framework Configuration
25
27
  framework: 'react',
26
28
  typescript: true,
29
+ componentType: 'functional',
30
+ // Processing Options
27
31
  watch: false,
32
+ parallel: true,
33
+ batchSize: 10,
34
+ maxConcurrency: 4,
35
+ cache: true,
36
+ // Default Properties
28
37
  defaultWidth: 24,
29
38
  defaultHeight: 24,
30
39
  defaultFill: 'currentColor',
31
- exclude: [],
40
+ defaultStroke: 'none',
41
+ defaultStrokeWidth: 1,
42
+ // Styling Configuration
32
43
  styleRules: {
33
44
  fill: 'inherit',
34
45
  stroke: 'none',
35
46
  },
36
- plugins: [],
37
- template: {
38
- type: 'default',
47
+ responsive: {
48
+ breakpoints: ['sm', 'md', 'lg', 'xl'],
49
+ values: {
50
+ width: ['16px', '20px', '24px', '32px'],
51
+ height: ['16px', '20px', '24px', '32px'],
52
+ },
39
53
  },
40
- frameworkOptions: {
41
- forwardRef: true,
42
- memo: false,
43
- scriptSetup: true,
44
- standalone: true,
54
+ theme: {
55
+ mode: 'auto',
56
+ variables: {
57
+ primary: 'currentColor',
58
+ secondary: '#6b7280',
59
+ accent: '#3b82f6',
60
+ },
45
61
  },
62
+ animations: [],
63
+ // Advanced Options
64
+ plugins: [],
65
+ exclude: [],
66
+ include: [],
67
+ // Error Handling
46
68
  errorHandling: {
47
- skipOnError: false,
48
- logLevel: 'info',
69
+ strategy: 'continue',
49
70
  maxRetries: 3,
71
+ timeout: 30000,
50
72
  },
73
+ // Performance Settings
51
74
  performance: {
52
- batchSize: 10,
53
- parallel: true,
54
- timeout: 30000,
55
- enableCache: true,
75
+ optimization: 'balanced',
76
+ memoryLimit: 512,
77
+ cacheTimeout: 3600000,
78
+ },
79
+ // Output Customization
80
+ outputConfig: {
81
+ naming: 'pascal',
82
+ extension: 'tsx',
83
+ directory: './src/components/icons',
84
+ },
85
+ // Framework-specific configurations
86
+ react: {
87
+ componentType: 'functional',
88
+ forwardRef: true,
89
+ memo: false,
90
+ propsInterface: 'SVGProps',
91
+ styledComponents: false,
92
+ cssModules: false,
93
+ },
94
+ vue: {
95
+ api: 'composition',
96
+ setup: true,
97
+ typescript: true,
98
+ scoped: true,
99
+ cssVariables: true,
100
+ },
101
+ angular: {
102
+ standalone: true,
103
+ signals: true,
104
+ changeDetection: 'OnPush',
105
+ encapsulation: 'Emulated',
56
106
  },
57
107
  };
58
108
  }
@@ -76,7 +76,8 @@ export class SVGService {
76
76
  defaultWidth: mergedConfig.defaultWidth,
77
77
  defaultHeight: mergedConfig.defaultHeight,
78
78
  defaultFill: mergedConfig.defaultFill,
79
- styleRules: Object.fromEntries(Object.entries(mergedConfig.styleRules || {}).filter(([_, v]) => v !== undefined)),
79
+ namingConvention: mergedConfig.output?.naming || 'pascal',
80
+ styleRules: Object.fromEntries(Object.entries(mergedConfig.styleRules || {}).filter(([, v]) => v !== undefined)),
80
81
  });
81
82
  results.push({
82
83
  success: processingResult.success,
@@ -107,7 +108,7 @@ export class SVGService {
107
108
  }
108
109
  // Generate index.ts file with all component exports
109
110
  if (successful > 0) {
110
- await this.generateIndexFile(outDir, results.filter(r => r.success).map(r => r.file));
111
+ await this.generateIndexFile(outDir, results.filter(r => r.success).map(r => r.file), mergedConfig);
111
112
  }
112
113
  }
113
114
  /**
@@ -134,7 +135,7 @@ export class SVGService {
134
135
  defaultWidth: mergedConfig.defaultWidth,
135
136
  defaultHeight: mergedConfig.defaultHeight,
136
137
  defaultFill: mergedConfig.defaultFill,
137
- styleRules: Object.fromEntries(Object.entries(mergedConfig.styleRules || {}).filter(([_, v]) => v !== undefined)),
138
+ styleRules: Object.fromEntries(Object.entries(mergedConfig.styleRules || {}).filter(([, v]) => v !== undefined)),
138
139
  });
139
140
  if (!result.success) {
140
141
  throw result.error || new Error('Failed to generate component');
@@ -202,7 +203,7 @@ export class SVGService {
202
203
  defaultWidth: mergedConfig.defaultWidth,
203
204
  defaultHeight: mergedConfig.defaultHeight,
204
205
  defaultFill: mergedConfig.defaultFill,
205
- styleRules: Object.fromEntries(Object.entries(mergedConfig.styleRules || {}).filter(([_, v]) => v !== undefined)),
206
+ styleRules: Object.fromEntries(Object.entries(mergedConfig.styleRules || {}).filter(([, v]) => v !== undefined)),
206
207
  });
207
208
  }
208
209
  catch (error) {
@@ -212,9 +213,10 @@ export class SVGService {
212
213
  /**
213
214
  * Handle file removal in watch mode
214
215
  */
215
- async handleFileRemoval(filePath, outDir) {
216
+ async handleFileRemoval(filePath, outDir, config) {
216
217
  try {
217
- const componentName = svgProcessor.generateComponentName(path.basename(filePath));
218
+ const namingConvention = config?.output?.naming || 'pascal';
219
+ const componentName = svgProcessor.generateComponentName(path.basename(filePath), namingConvention);
218
220
  const componentPath = path.join(outDir, `${componentName}.tsx`);
219
221
  if (await FileSystem.exists(componentPath)) {
220
222
  await FileSystem.unlink(componentPath);
@@ -256,11 +258,12 @@ export class SVGService {
256
258
  /**
257
259
  * Generate index.ts file with all component exports
258
260
  */
259
- async generateIndexFile(outDir, svgFiles) {
261
+ async generateIndexFile(outDir, svgFiles, config) {
260
262
  try {
263
+ const namingConvention = config?.output?.naming || 'pascal';
261
264
  const componentNames = svgFiles.map(file => {
262
265
  const baseName = path.basename(file, '.svg');
263
- return svgProcessor.generateComponentName(baseName);
266
+ return svgProcessor.generateComponentName(baseName, namingConvention);
264
267
  });
265
268
  const indexContent = this.generateIndexContent(componentNames);
266
269
  const indexPath = path.join(outDir, 'index.ts');
@@ -278,17 +281,6 @@ export class SVGService {
278
281
  const imports = componentNames
279
282
  .map(name => `export { default as ${name} } from './${name}';`)
280
283
  .join('\n');
281
- const exportAll = `
282
- // Export all components
283
- export {
284
- ${componentNames.map(name => ` ${name},`).join('\n')}
285
- };
286
-
287
- // Re-export for convenience
288
- export default {
289
- ${componentNames.map(name => ` ${name},`).join('\n')}
290
- };
291
- `;
292
284
  return `/**
293
285
  * SVG Components Index
294
286
  * Generated by svger-cli
@@ -298,10 +290,10 @@ ${componentNames.map(name => ` ${name},`).join('\n')}
298
290
  *
299
291
  * Import all components:
300
292
  * import * as Icons from './components';
301
- * import Icons from './components'; // default export
302
293
  */
303
294
 
304
- ${imports}${exportAll}`;
295
+ ${imports}
296
+ `;
305
297
  }
306
298
  /**
307
299
  * Get service statistics
@@ -3,40 +3,91 @@
3
3
  */
4
4
  export type FrameworkType = 'react' | 'vue' | 'svelte' | 'angular' | 'solid' | 'preact' | 'lit' | 'vanilla';
5
5
  export type NamingConvention = 'kebab' | 'pascal' | 'camel';
6
+ export type ComponentType = 'functional' | 'class' | 'arrow';
7
+ export type ErrorHandlingStrategy = 'continue' | 'stop' | 'retry';
8
+ export type PerformanceOptimization = 'fast' | 'balanced' | 'maximum';
9
+ export type ThemeMode = 'light' | 'dark' | 'auto';
10
+ export type ChangeDetectionStrategy = 'Default' | 'OnPush';
11
+ export type ViewEncapsulation = 'Emulated' | 'None' | 'ShadowDom';
6
12
  export interface OutputConfig {
7
13
  naming?: NamingConvention;
8
14
  extension?: string;
9
15
  directory?: string;
10
16
  }
17
+ export interface ResponsiveConfig {
18
+ breakpoints: string[];
19
+ values: {
20
+ [property: string]: string[];
21
+ };
22
+ }
23
+ export interface ThemeConfig {
24
+ mode: ThemeMode;
25
+ variables: {
26
+ [name: string]: string;
27
+ };
28
+ }
29
+ export interface ErrorHandlingConfig {
30
+ strategy: ErrorHandlingStrategy;
31
+ maxRetries: number;
32
+ timeout: number;
33
+ }
34
+ export interface PerformanceConfig {
35
+ optimization: PerformanceOptimization;
36
+ memoryLimit: number;
37
+ cacheTimeout: number;
38
+ }
39
+ export interface ReactConfig {
40
+ componentType?: ComponentType;
41
+ forwardRef?: boolean;
42
+ memo?: boolean;
43
+ propsInterface?: string;
44
+ styledComponents?: boolean;
45
+ cssModules?: boolean;
46
+ }
47
+ export interface VueConfig {
48
+ api?: 'composition' | 'options';
49
+ setup?: boolean;
50
+ typescript?: boolean;
51
+ scoped?: boolean;
52
+ cssVariables?: boolean;
53
+ }
54
+ export interface AngularConfig {
55
+ standalone?: boolean;
56
+ signals?: boolean;
57
+ changeDetection?: ChangeDetectionStrategy;
58
+ encapsulation?: ViewEncapsulation;
59
+ }
11
60
  export interface SVGConfig {
12
61
  source: string;
13
- output: string | OutputConfig;
14
- watch: boolean;
62
+ output: string;
15
63
  framework: FrameworkType;
16
64
  typescript: boolean;
65
+ componentType?: ComponentType;
66
+ watch: boolean;
67
+ parallel: boolean;
68
+ batchSize: number;
69
+ maxConcurrency: number;
70
+ cache: boolean;
17
71
  defaultWidth: number;
18
72
  defaultHeight: number;
19
73
  defaultFill: string;
20
- exclude: string[];
74
+ defaultStroke?: string;
75
+ defaultStrokeWidth?: number;
21
76
  styleRules: {
22
- fill?: string;
23
- stroke?: string;
24
- [key: string]: string | undefined;
25
- };
26
- plugins?: PluginConfig[];
27
- template?: TemplateConfig;
28
- frameworkOptions?: FrameworkOptions;
29
- errorHandling?: {
30
- skipOnError: boolean;
31
- logLevel: 'debug' | 'info' | 'warn' | 'error';
32
- maxRetries: number;
33
- };
34
- performance?: {
35
- batchSize: number;
36
- parallel: boolean;
37
- timeout: number;
38
- enableCache: boolean;
77
+ [property: string]: string;
39
78
  };
79
+ responsive?: ResponsiveConfig;
80
+ theme?: ThemeConfig;
81
+ animations?: string[];
82
+ plugins: PluginConfig[];
83
+ exclude: string[];
84
+ include?: string[];
85
+ errorHandling: ErrorHandlingConfig;
86
+ performance: PerformanceConfig;
87
+ outputConfig: OutputConfig;
88
+ react?: ReactConfig;
89
+ vue?: VueConfig;
90
+ angular?: AngularConfig;
40
91
  }
41
92
  export interface FrameworkOptions {
42
93
  composition?: boolean;
@@ -78,6 +129,7 @@ export interface ComponentGenerationOptions {
78
129
  styleRules?: Record<string, string>;
79
130
  template?: TemplateConfig;
80
131
  frameworkOptions?: FrameworkOptions;
132
+ namingConvention?: 'kebab' | 'pascal' | 'camel';
81
133
  }
82
134
  export interface TemplateConfig {
83
135
  type: 'default' | 'custom';
@@ -1,9 +1,9 @@
1
+ type BufferEncoding = 'ascii' | 'utf8' | 'utf-8' | 'utf16le' | 'ucs2' | 'ucs-2' | 'base64' | 'base64url' | 'latin1' | 'binary' | 'hex';
1
2
  /**
2
3
  * Native Node.js utilities to replace external dependencies
3
4
  */
4
5
  /**
5
- * Convert a string to PascalCase.
6
- * Handles kebab-case, snake_case, and space-separated strings.
6
+ * Convert a string to PascalCase, preserving existing capital letters.
7
7
  *
8
8
  * @param {string} str - Input string to convert.
9
9
  * @returns {string} PascalCase string.
@@ -12,6 +12,7 @@
12
12
  * toPascalCase('hello-world') => 'HelloWorld'
13
13
  * toPascalCase('hello_world') => 'HelloWorld'
14
14
  * toPascalCase('hello world') => 'HelloWorld'
15
+ * toPascalCase('ArrowBendDownLeft') => 'ArrowBendDownLeft'
15
16
  */
16
17
  export declare function toPascalCase(str: string): string;
17
18
  /**
@@ -4,8 +4,7 @@ import { promisify } from 'util';
4
4
  * Native Node.js utilities to replace external dependencies
5
5
  */
6
6
  /**
7
- * Convert a string to PascalCase.
8
- * Handles kebab-case, snake_case, and space-separated strings.
7
+ * Convert a string to PascalCase, preserving existing capital letters.
9
8
  *
10
9
  * @param {string} str - Input string to convert.
11
10
  * @returns {string} PascalCase string.
@@ -14,8 +13,13 @@ import { promisify } from 'util';
14
13
  * toPascalCase('hello-world') => 'HelloWorld'
15
14
  * toPascalCase('hello_world') => 'HelloWorld'
16
15
  * toPascalCase('hello world') => 'HelloWorld'
16
+ * toPascalCase('ArrowBendDownLeft') => 'ArrowBendDownLeft'
17
17
  */
18
18
  export function toPascalCase(str) {
19
+ // If the string is already in PascalCase format (no separators and starts with capital), preserve it
20
+ if (/^[A-Z]/.test(str) && !/[-_\s]/.test(str)) {
21
+ return str;
22
+ }
19
23
  return str
20
24
  .replace(/[-_\s]+(.)?/g, (_, char) => (char ? char.toUpperCase() : ''))
21
25
  .replace(/^(.)/, char => char.toUpperCase());
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "svger-cli",
3
- "version": "2.0.3",
3
+ "version": "2.0.5",
4
4
  "description": "Enterprise-grade, zero-dependency SVG to component converter supporting React, Vue, Angular, Svelte, Solid, Lit, Preact & Vanilla. Features auto-generated TypeScript exports, responsive design, themes, performance optimization & 85% faster processing than SVGR.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",