svger-cli 2.0.2 → 2.0.4

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.
@@ -20,40 +20,90 @@ 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
  },
47
+ responsive: {
48
+ breakpoints: ['sm', 'md', 'lg', 'xl'],
49
+ values: {
50
+ width: ['16px', '20px', '24px', '32px'],
51
+ height: ['16px', '20px', '24px', '32px'],
52
+ },
53
+ },
54
+ theme: {
55
+ mode: 'auto',
56
+ variables: {
57
+ primary: 'currentColor',
58
+ secondary: '#6b7280',
59
+ accent: '#3b82f6',
60
+ },
61
+ },
62
+ animations: [],
63
+ // Advanced Options
36
64
  plugins: [],
37
- template: {
38
- type: 'default'
65
+ exclude: [],
66
+ include: [],
67
+ // Error Handling
68
+ errorHandling: {
69
+ strategy: 'continue',
70
+ maxRetries: 3,
71
+ timeout: 30000,
72
+ },
73
+ // Performance Settings
74
+ performance: {
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',
39
84
  },
40
- frameworkOptions: {
85
+ // Framework-specific configurations
86
+ react: {
87
+ componentType: 'functional',
41
88
  forwardRef: true,
42
89
  memo: false,
43
- scriptSetup: true,
44
- standalone: true
90
+ propsInterface: 'SVGProps',
91
+ styledComponents: false,
92
+ cssModules: false,
45
93
  },
46
- errorHandling: {
47
- skipOnError: false,
48
- logLevel: 'info',
49
- maxRetries: 3
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',
50
106
  },
51
- performance: {
52
- batchSize: 10,
53
- parallel: true,
54
- timeout: 30000,
55
- enableCache: true
56
- }
57
107
  };
58
108
  }
59
109
  /**
@@ -79,7 +129,7 @@ export class ConfigService {
79
129
  // Merge with defaults to ensure all required properties exist
80
130
  this.cachedConfig = {
81
131
  ...this.getDefaultConfig(),
82
- ...configData
132
+ ...configData,
83
133
  };
84
134
  logger.debug('Configuration loaded successfully');
85
135
  return this.cachedConfig;
@@ -165,7 +215,8 @@ export class ConfigService {
165
215
  // Required string fields
166
216
  const requiredStringFields = ['source', 'output', 'defaultFill'];
167
217
  for (const field of requiredStringFields) {
168
- if (!configToValidate[field] || typeof configToValidate[field] !== 'string') {
218
+ if (!configToValidate[field] ||
219
+ typeof configToValidate[field] !== 'string') {
169
220
  errors.push(`${field} must be a non-empty string`);
170
221
  }
171
222
  }
@@ -182,12 +233,13 @@ export class ConfigService {
182
233
  errors.push('exclude must be an array');
183
234
  }
184
235
  // Validate styleRules object
185
- if (configToValidate.styleRules && typeof configToValidate.styleRules !== 'object') {
236
+ if (configToValidate.styleRules &&
237
+ typeof configToValidate.styleRules !== 'object') {
186
238
  errors.push('styleRules must be an object');
187
239
  }
188
240
  return {
189
241
  valid: errors.length === 0,
190
- errors
242
+ errors,
191
243
  };
192
244
  }
193
245
  /**
@@ -30,7 +30,7 @@ export class FileWatcherService {
30
30
  try {
31
31
  const watcher = fs.watch(resolvedPath, {
32
32
  persistent: true,
33
- recursive: false
33
+ recursive: false,
34
34
  }, (eventType, filename) => {
35
35
  if (filename) {
36
36
  this.handleFileEvent(watchId, eventType, path.join(resolvedPath, filename));
@@ -83,7 +83,7 @@ export class FileWatcherService {
83
83
  const event = {
84
84
  type: actualEventType,
85
85
  filePath,
86
- timestamp: Date.now()
86
+ timestamp: Date.now(),
87
87
  };
88
88
  logger.debug(`File event: ${actualEventType} - ${path.basename(filePath)}`);
89
89
  // Emit event to registered handlers
@@ -160,7 +160,7 @@ export class FileWatcherService {
160
160
  return {
161
161
  activeWatchers: this.watchers.size,
162
162
  pendingEvents: this.debounceTimers.size,
163
- totalHandlers
163
+ totalHandlers,
164
164
  };
165
165
  }
166
166
  /**
@@ -41,9 +41,15 @@ export class SVGService {
41
41
  ...config,
42
42
  ...(options.config || {}),
43
43
  // Support direct properties on options for CLI convenience
44
- ...options.framework && { framework: options.framework },
45
- ...options.typescript !== undefined && { typescript: options.typescript },
46
- ...options.frameworkOptions && { frameworkOptions: options.frameworkOptions }
44
+ ...(options.framework && {
45
+ framework: options.framework,
46
+ }),
47
+ ...(options.typescript !== undefined && {
48
+ typescript: options.typescript,
49
+ }),
50
+ ...(options.frameworkOptions && {
51
+ frameworkOptions: options.frameworkOptions,
52
+ }),
47
53
  };
48
54
  // Read all SVG files
49
55
  const files = await FileSystem.readDir(srcDir);
@@ -70,12 +76,13 @@ export class SVGService {
70
76
  defaultWidth: mergedConfig.defaultWidth,
71
77
  defaultHeight: mergedConfig.defaultHeight,
72
78
  defaultFill: mergedConfig.defaultFill,
73
- 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)),
74
81
  });
75
82
  results.push({
76
83
  success: processingResult.success,
77
84
  file,
78
- error: processingResult.error
85
+ error: processingResult.error,
79
86
  });
80
87
  }
81
88
  catch (error) {
@@ -83,7 +90,7 @@ export class SVGService {
83
90
  results.push({
84
91
  success: false,
85
92
  file,
86
- error: error
93
+ error: error,
87
94
  });
88
95
  }
89
96
  }
@@ -93,13 +100,15 @@ export class SVGService {
93
100
  logger.info(`Build complete: ${successful} successful, ${failed} failed`);
94
101
  if (failed > 0) {
95
102
  logger.warn('Some files failed to process:');
96
- results.filter(r => !r.success).forEach(r => {
103
+ results
104
+ .filter(r => !r.success)
105
+ .forEach(r => {
97
106
  logger.warn(` - ${r.file}: ${r.error?.message}`);
98
107
  });
99
108
  }
100
109
  // Generate index.ts file with all component exports
101
110
  if (successful > 0) {
102
- 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);
103
112
  }
104
113
  }
105
114
  /**
@@ -126,7 +135,7 @@ export class SVGService {
126
135
  defaultWidth: mergedConfig.defaultWidth,
127
136
  defaultHeight: mergedConfig.defaultHeight,
128
137
  defaultFill: mergedConfig.defaultFill,
129
- styleRules: Object.fromEntries(Object.entries(mergedConfig.styleRules || {}).filter(([_, v]) => v !== undefined))
138
+ styleRules: Object.fromEntries(Object.entries(mergedConfig.styleRules || {}).filter(([, v]) => v !== undefined)),
130
139
  });
131
140
  if (!result.success) {
132
141
  throw result.error || new Error('Failed to generate component');
@@ -194,7 +203,7 @@ export class SVGService {
194
203
  defaultWidth: mergedConfig.defaultWidth,
195
204
  defaultHeight: mergedConfig.defaultHeight,
196
205
  defaultFill: mergedConfig.defaultFill,
197
- styleRules: Object.fromEntries(Object.entries(mergedConfig.styleRules || {}).filter(([_, v]) => v !== undefined))
206
+ styleRules: Object.fromEntries(Object.entries(mergedConfig.styleRules || {}).filter(([, v]) => v !== undefined)),
198
207
  });
199
208
  }
200
209
  catch (error) {
@@ -204,9 +213,10 @@ export class SVGService {
204
213
  /**
205
214
  * Handle file removal in watch mode
206
215
  */
207
- async handleFileRemoval(filePath, outDir) {
216
+ async handleFileRemoval(filePath, outDir, config) {
208
217
  try {
209
- const componentName = svgProcessor.generateComponentName(path.basename(filePath));
218
+ const namingConvention = config?.output?.naming || 'pascal';
219
+ const componentName = svgProcessor.generateComponentName(path.basename(filePath), namingConvention);
210
220
  const componentPath = path.join(outDir, `${componentName}.tsx`);
211
221
  if (await FileSystem.exists(componentPath)) {
212
222
  await FileSystem.unlink(componentPath);
@@ -248,11 +258,12 @@ export class SVGService {
248
258
  /**
249
259
  * Generate index.ts file with all component exports
250
260
  */
251
- async generateIndexFile(outDir, svgFiles) {
261
+ async generateIndexFile(outDir, svgFiles, config) {
252
262
  try {
263
+ const namingConvention = config?.output?.naming || 'pascal';
253
264
  const componentNames = svgFiles.map(file => {
254
265
  const baseName = path.basename(file, '.svg');
255
- return svgProcessor.generateComponentName(baseName);
266
+ return svgProcessor.generateComponentName(baseName, namingConvention);
256
267
  });
257
268
  const indexContent = this.generateIndexContent(componentNames);
258
269
  const indexPath = path.join(outDir, 'index.ts');
@@ -267,18 +278,9 @@ export class SVGService {
267
278
  * Generate the content for index.ts file
268
279
  */
269
280
  generateIndexContent(componentNames) {
270
- const imports = componentNames.map(name => `export { default as ${name} } from './${name}';`).join('\n');
271
- const exportAll = `
272
- // Export all components
273
- export {
274
- ${componentNames.map(name => ` ${name},`).join('\n')}
275
- };
276
-
277
- // Re-export for convenience
278
- export default {
279
- ${componentNames.map(name => ` ${name},`).join('\n')}
280
- };
281
- `;
281
+ const imports = componentNames
282
+ .map(name => `export { default as ${name} } from './${name}';`)
283
+ .join('\n');
282
284
  return `/**
283
285
  * SVG Components Index
284
286
  * Generated by svger-cli
@@ -288,10 +290,10 @@ ${componentNames.map(name => ` ${name},`).join('\n')}
288
290
  *
289
291
  * Import all components:
290
292
  * import * as Icons from './components';
291
- * import Icons from './components'; // default export
292
293
  */
293
294
 
294
- ${imports}${exportAll}`;
295
+ ${imports}
296
+ `;
295
297
  }
296
298
  /**
297
299
  * Get service statistics
@@ -300,7 +302,7 @@ ${imports}${exportAll}`;
300
302
  return {
301
303
  activeWatchers: this.activeWatchers.size,
302
304
  processingQueue: svgProcessor.getProcessingStats(),
303
- watcherStats: fileWatcher.getWatchStats()
305
+ watcherStats: fileWatcher.getWatchStats(),
304
306
  };
305
307
  }
306
308
  /**
@@ -346,7 +348,9 @@ export class LockService {
346
348
  }
347
349
  writeLockFile(locks) {
348
350
  try {
349
- FileSystem.writeJSONSync(this.getLockFilePath(), Array.from(locks), { spaces: 2 });
351
+ FileSystem.writeJSONSync(this.getLockFilePath(), Array.from(locks), {
352
+ spaces: 2,
353
+ });
350
354
  this.cachedLocks = locks;
351
355
  }
352
356
  catch (error) {
@@ -13,32 +13,32 @@
13
13
  * @param [options.defaultFill="currentColor"] - The default fill color for the SVG.
14
14
  * @returns A string containing the complete TypeScript code for the React SVG component.
15
15
  */
16
- export function reactTemplate({ componentName, svgContent, defaultWidth = 24, defaultHeight = 24, defaultFill = "currentColor", }) {
16
+ export function reactTemplate({ componentName, svgContent, defaultWidth = 24, defaultHeight = 24, defaultFill = 'currentColor', }) {
17
17
  const cleaned = svgContent
18
- .replace(/<\?xml.*?\?>/g, "")
19
- .replace(/<!DOCTYPE.*?>/g, "")
20
- .replace(/\r?\n|\r/g, "")
21
- .replace(/\s{2,}/g, " ")
22
- .replace(/style="[^"]*"/g, "")
23
- .replace(/\s+xmlns(:xlink)?="[^"]*"/g, "")
18
+ .replace(/<\?xml.*?\?>/g, '')
19
+ .replace(/<!DOCTYPE.*?>/g, '')
20
+ .replace(/\r?\n|\r/g, '')
21
+ .replace(/\s{2,}/g, ' ')
22
+ .replace(/style="[^"]*"/g, '')
23
+ .replace(/\s+xmlns(:xlink)?="[^"]*"/g, '')
24
24
  .trim()
25
- .replace(/^.*?<svg[^>]*>(.*?)<\/svg>.*$/i, "$1");
26
- return `import type { SVGProps } from "react";
27
- const Svg${componentName} = (props: SVGProps<SVGSVGElement>) => (
28
- <svg
29
- viewBox="0 0 ${defaultWidth} ${defaultHeight}"
30
- xmlns="http://www.w3.org/2000/svg"
31
- xmlnsXlink="http://www.w3.org/1999/xlink"
32
- width={props.width || ${defaultWidth}}
33
- height={props.height || ${defaultHeight}}
34
- fill={props.fill || "${defaultFill}"}
35
- stroke={props.stroke || "none"}
36
- className={props.className}
37
- {...props}
38
- >
39
- ${cleaned}
40
- </svg>
41
- );
42
- export default Svg${componentName};
25
+ .replace(/^.*?<svg[^>]*>(.*?)<\/svg>.*$/i, '$1');
26
+ return `import type { SVGProps } from "react";
27
+ const Svg${componentName} = (props: SVGProps<SVGSVGElement>) => (
28
+ <svg
29
+ viewBox="0 0 ${defaultWidth} ${defaultHeight}"
30
+ xmlns="http://www.w3.org/2000/svg"
31
+ xmlnsXlink="http://www.w3.org/1999/xlink"
32
+ width={props.width || ${defaultWidth}}
33
+ height={props.height || ${defaultHeight}}
34
+ fill={props.fill || "${defaultFill}"}
35
+ stroke={props.stroke || "none"}
36
+ className={props.className}
37
+ {...props}
38
+ >
39
+ ${cleaned}
40
+ </svg>
41
+ );
42
+ export default Svg${componentName};
43
43
  `;
44
44
  }
@@ -2,35 +2,92 @@
2
2
  * Type definitions for svger-cli
3
3
  */
4
4
  export type FrameworkType = 'react' | 'vue' | 'svelte' | 'angular' | 'solid' | 'preact' | 'lit' | 'vanilla';
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';
12
+ export interface OutputConfig {
13
+ naming?: NamingConvention;
14
+ extension?: string;
15
+ directory?: string;
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
+ }
5
60
  export interface SVGConfig {
6
61
  source: string;
7
62
  output: string;
8
- watch: boolean;
9
63
  framework: FrameworkType;
10
64
  typescript: boolean;
65
+ componentType?: ComponentType;
66
+ watch: boolean;
67
+ parallel: boolean;
68
+ batchSize: number;
69
+ maxConcurrency: number;
70
+ cache: boolean;
11
71
  defaultWidth: number;
12
72
  defaultHeight: number;
13
73
  defaultFill: string;
14
- exclude: string[];
74
+ defaultStroke?: string;
75
+ defaultStrokeWidth?: number;
15
76
  styleRules: {
16
- fill?: string;
17
- stroke?: string;
18
- [key: string]: string | undefined;
19
- };
20
- plugins?: PluginConfig[];
21
- template?: TemplateConfig;
22
- frameworkOptions?: FrameworkOptions;
23
- errorHandling?: {
24
- skipOnError: boolean;
25
- logLevel: 'debug' | 'info' | 'warn' | 'error';
26
- maxRetries: number;
27
- };
28
- performance?: {
29
- batchSize: number;
30
- parallel: boolean;
31
- timeout: number;
32
- enableCache: boolean;
77
+ [property: string]: string;
33
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;
34
91
  }
35
92
  export interface FrameworkOptions {
36
93
  composition?: boolean;
@@ -72,6 +129,7 @@ export interface ComponentGenerationOptions {
72
129
  styleRules?: Record<string, string>;
73
130
  template?: TemplateConfig;
74
131
  frameworkOptions?: FrameworkOptions;
132
+ namingConvention?: 'kebab' | 'pascal' | 'camel';
75
133
  }
76
134
  export interface TemplateConfig {
77
135
  type: 'default' | 'custom';
@@ -1,10 +1,41 @@
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 string to PascalCase (replaces change-case package)
6
+ * Convert a string to PascalCase, preserving existing capital letters.
7
+ *
8
+ * @param {string} str - Input string to convert.
9
+ * @returns {string} PascalCase string.
10
+ *
11
+ * @example
12
+ * toPascalCase('hello-world') => 'HelloWorld'
13
+ * toPascalCase('hello_world') => 'HelloWorld'
14
+ * toPascalCase('hello world') => 'HelloWorld'
15
+ * toPascalCase('ArrowBendDownLeft') => 'ArrowBendDownLeft'
6
16
  */
7
17
  export declare function toPascalCase(str: string): string;
18
+ /**
19
+ * Convert a string to camelCase.
20
+ *
21
+ * @param {string} str - Input string to convert.
22
+ * @returns {string} camelCase string.
23
+ *
24
+ * @example
25
+ * toCamelCase('hello-world') => 'helloWorld'
26
+ */
27
+ export declare function toCamelCase(str: string): string;
28
+ /**
29
+ * Convert a string to kebab-case.
30
+ *
31
+ * @param {string} str - Input string to convert.
32
+ * @returns {string} kebab-case string.
33
+ *
34
+ * @example
35
+ * toKebabCase('HelloWorld') => 'hello-world'
36
+ * toKebabCase('hello_world') => 'hello-world'
37
+ */
38
+ export declare function toKebabCase(str: string): string;
8
39
  /**
9
40
  * Native file system utilities (replaces fs-extra package)
10
41
  */
@@ -4,15 +4,54 @@ import { promisify } from 'util';
4
4
  * Native Node.js utilities to replace external dependencies
5
5
  */
6
6
  /**
7
- * Convert string to PascalCase (replaces change-case package)
7
+ * Convert a string to PascalCase, preserving existing capital letters.
8
+ *
9
+ * @param {string} str - Input string to convert.
10
+ * @returns {string} PascalCase string.
11
+ *
12
+ * @example
13
+ * toPascalCase('hello-world') => 'HelloWorld'
14
+ * toPascalCase('hello_world') => 'HelloWorld'
15
+ * toPascalCase('hello world') => 'HelloWorld'
16
+ * toPascalCase('ArrowBendDownLeft') => 'ArrowBendDownLeft'
8
17
  */
9
18
  export function toPascalCase(str) {
19
+ // If the string is already in a good format (contains capital letters and no separators), preserve it
20
+ if (/^[A-Z][a-zA-Z0-9]*$/.test(str)) {
21
+ return str;
22
+ }
23
+ return str
24
+ .replace(/[-_\s]+(.)?/g, (_, char) => (char ? char.toUpperCase() : ''))
25
+ .replace(/^(.)/, char => char.toUpperCase());
26
+ }
27
+ /**
28
+ * Convert a string to camelCase.
29
+ *
30
+ * @param {string} str - Input string to convert.
31
+ * @returns {string} camelCase string.
32
+ *
33
+ * @example
34
+ * toCamelCase('hello-world') => 'helloWorld'
35
+ */
36
+ export function toCamelCase(str) {
37
+ const pascal = toPascalCase(str);
38
+ return pascal.charAt(0).toLowerCase() + pascal.slice(1);
39
+ }
40
+ /**
41
+ * Convert a string to kebab-case.
42
+ *
43
+ * @param {string} str - Input string to convert.
44
+ * @returns {string} kebab-case string.
45
+ *
46
+ * @example
47
+ * toKebabCase('HelloWorld') => 'hello-world'
48
+ * toKebabCase('hello_world') => 'hello-world'
49
+ */
50
+ export function toKebabCase(str) {
10
51
  return str
11
- .replace(/[^a-zA-Z0-9]/g, ' ')
12
- .split(' ')
13
- .filter(Boolean)
14
- .map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
15
- .join('');
52
+ .replace(/([a-z])([A-Z])/g, '$1-$2')
53
+ .replace(/[\s_]+/g, '-')
54
+ .toLowerCase();
16
55
  }
17
56
  /**
18
57
  * Native file system utilities (replaces fs-extra package)
@@ -154,7 +193,7 @@ export class CLI {
154
193
  this.commands.set(command, {
155
194
  description,
156
195
  action: action,
157
- options
196
+ options,
158
197
  });
159
198
  }
160
199
  async parse() {
@@ -266,7 +305,7 @@ export class FileWatcher {
266
305
  try {
267
306
  const watcher = fs.watch(path, {
268
307
  recursive: options?.recursive || false,
269
- persistent: true
308
+ persistent: true,
270
309
  }, (eventType, filename) => {
271
310
  if (filename) {
272
311
  this.emit(eventType, `${path}/${filename}`);
package/dist/watch.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { FileWatcher } from "./utils/native.js";
1
+ import { FileWatcher } from './utils/native.js';
2
2
  /**
3
3
  * Watches a source folder for changes to SVG files and automatically
4
4
  * rebuilds React components when SVGs are added, modified, or deleted.