svger-cli 1.0.6 → 1.0.7

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.
Files changed (64) hide show
  1. package/CODE_OF_CONDUCT.md +79 -0
  2. package/CONTRIBUTING.md +146 -0
  3. package/LICENSE +21 -0
  4. package/README.md +1862 -73
  5. package/TESTING.md +143 -0
  6. package/cli-framework.test.js +16 -0
  7. package/cli-test-angular/Arrowbenddownleft.component.ts +27 -0
  8. package/cli-test-angular/Vite.component.ts +27 -0
  9. package/cli-test-angular/index.ts +25 -0
  10. package/{my-icons/ArrowBendDownLeft.tsx → cli-test-output/Arrowbenddownleft.vue} +28 -12
  11. package/{my-icons/Vite.tsx → cli-test-output/Vite.vue} +28 -12
  12. package/cli-test-output/index.ts +25 -0
  13. package/cli-test-react/Arrowbenddownleft.tsx +39 -0
  14. package/cli-test-react/Vite.tsx +39 -0
  15. package/cli-test-react/index.ts +25 -0
  16. package/cli-test-svelte/Arrowbenddownleft.svelte +22 -0
  17. package/cli-test-svelte/Vite.svelte +22 -0
  18. package/cli-test-svelte/index.ts +25 -0
  19. package/dist/builder.js +12 -13
  20. package/dist/clean.js +3 -3
  21. package/dist/cli.js +139 -61
  22. package/dist/config.d.ts +1 -1
  23. package/dist/config.js +5 -7
  24. package/dist/lock.js +1 -1
  25. package/dist/templates/ComponentTemplate.d.ts +15 -0
  26. package/dist/templates/ComponentTemplate.js +15 -0
  27. package/dist/watch.d.ts +2 -1
  28. package/dist/watch.js +30 -33
  29. package/docs/ADR-SVG-INTRGRATION-METHODS-002.adr.md +550 -0
  30. package/docs/FRAMEWORK-GUIDE.md +768 -0
  31. package/docs/IMPLEMENTATION-SUMMARY.md +376 -0
  32. package/frameworks.test.js +170 -0
  33. package/package.json +7 -10
  34. package/src/builder.ts +12 -13
  35. package/src/clean.ts +3 -3
  36. package/src/cli.ts +148 -59
  37. package/src/config.ts +5 -6
  38. package/src/core/error-handler.ts +303 -0
  39. package/src/core/framework-templates.ts +428 -0
  40. package/src/core/logger.ts +104 -0
  41. package/src/core/performance-engine.ts +327 -0
  42. package/src/core/plugin-manager.ts +228 -0
  43. package/src/core/style-compiler.ts +605 -0
  44. package/src/core/template-manager.ts +619 -0
  45. package/src/index.ts +235 -0
  46. package/src/lock.ts +1 -1
  47. package/src/processors/svg-processor.ts +288 -0
  48. package/src/services/config.ts +241 -0
  49. package/src/services/file-watcher.ts +218 -0
  50. package/src/services/svg-service.ts +468 -0
  51. package/src/types/index.ts +169 -0
  52. package/src/utils/native.ts +352 -0
  53. package/src/watch.ts +36 -36
  54. package/test-output-mulit/TestIcon-angular-module.component.ts +26 -0
  55. package/test-output-mulit/TestIcon-angular-standalone.component.ts +27 -0
  56. package/test-output-mulit/TestIcon-lit.ts +35 -0
  57. package/test-output-mulit/TestIcon-preact.tsx +38 -0
  58. package/test-output-mulit/TestIcon-react.tsx +35 -0
  59. package/test-output-mulit/TestIcon-solid.tsx +27 -0
  60. package/test-output-mulit/TestIcon-svelte.svelte +22 -0
  61. package/test-output-mulit/TestIcon-vanilla.ts +37 -0
  62. package/test-output-mulit/TestIcon-vue-composition.vue +33 -0
  63. package/test-output-mulit/TestIcon-vue-options.vue +31 -0
  64. package/tsconfig.json +11 -9
@@ -0,0 +1,468 @@
1
+ import path from 'path';
2
+ import { FileSystem } from '../utils/native.js';
3
+ import { BuildOptions, GenerateOptions, WatchOptions, FileWatchEvent } from '../types/index.js';
4
+ import { logger } from '../core/logger.js';
5
+ import { configService } from './config.js';
6
+ import { svgProcessor } from '../processors/svg-processor.js';
7
+ import { fileWatcher } from './file-watcher.js';
8
+
9
+ /**
10
+ * Main SVG service that orchestrates all SVG processing operations
11
+ */
12
+ export class SVGService {
13
+ private static instance: SVGService;
14
+ private activeWatchers: Set<string> = new Set();
15
+ public lockService: LockService;
16
+
17
+ private constructor() {
18
+ this.lockService = LockService.getInstance();
19
+ }
20
+
21
+ public static getInstance(): SVGService {
22
+ if (!SVGService.instance) {
23
+ SVGService.instance = new SVGService();
24
+ }
25
+ return SVGService.instance;
26
+ }
27
+
28
+ /**
29
+ * Build all SVG files from source to output directory
30
+ */
31
+ public async buildAll(options: BuildOptions): Promise<void> {
32
+ logger.info('Starting SVG build process');
33
+ logger.info(`Source: ${options.src}`);
34
+ logger.info(`Output: ${options.out}`);
35
+
36
+ const srcDir = path.resolve(options.src);
37
+ const outDir = path.resolve(options.out);
38
+
39
+ // Validate source directory
40
+ if (!(await FileSystem.exists(srcDir))) {
41
+ throw new Error(`Source folder not found: ${srcDir}`);
42
+ }
43
+
44
+ // Ensure output directory exists
45
+ await FileSystem.ensureDir(outDir);
46
+
47
+ // Get configuration - merge config file with options
48
+ const config = configService.readConfig();
49
+ const mergedConfig = {
50
+ ...config,
51
+ ...(options.config || {}),
52
+ // Support direct properties on options for CLI convenience
53
+ ...(options as any).framework && { framework: (options as any).framework },
54
+ ...(options as any).typescript !== undefined && { typescript: (options as any).typescript },
55
+ ...(options as any).frameworkOptions && { frameworkOptions: (options as any).frameworkOptions }
56
+ };
57
+
58
+ // Read all SVG files
59
+ const files = await FileSystem.readDir(srcDir);
60
+ const svgFiles = files.filter((file: string) => file.endsWith('.svg'));
61
+
62
+ if (svgFiles.length === 0) {
63
+ logger.warn('No SVG files found in source directory');
64
+ return;
65
+ }
66
+
67
+ logger.info(`Found ${svgFiles.length} SVG files to process`);
68
+
69
+ const results: Array<{ success: boolean; file: string; error?: Error }> = [];
70
+
71
+ // Process each SVG file
72
+ for (const file of svgFiles) {
73
+ const svgPath = path.join(srcDir, file);
74
+
75
+ // Check if file is locked
76
+ if (this.lockService.isLocked(svgPath)) {
77
+ logger.warn(`Skipped locked file: ${file}`);
78
+ continue;
79
+ }
80
+
81
+ try {
82
+ const processingResult = await svgProcessor.processSVGFile(svgPath, outDir, {
83
+ framework: mergedConfig.framework,
84
+ typescript: mergedConfig.typescript,
85
+ frameworkOptions: mergedConfig.frameworkOptions,
86
+ defaultWidth: mergedConfig.defaultWidth,
87
+ defaultHeight: mergedConfig.defaultHeight,
88
+ defaultFill: mergedConfig.defaultFill,
89
+ styleRules: Object.fromEntries(
90
+ Object.entries(mergedConfig.styleRules || {}).filter(([_, v]) => v !== undefined)
91
+ ) as Record<string, string>
92
+ });
93
+
94
+ results.push({
95
+ success: processingResult.success,
96
+ file,
97
+ error: processingResult.error
98
+ });
99
+
100
+ } catch (error) {
101
+ logger.error(`Failed to process ${file}:`, error);
102
+ results.push({
103
+ success: false,
104
+ file,
105
+ error: error as Error
106
+ });
107
+ }
108
+ }
109
+
110
+ // Log summary
111
+ const successful = results.filter(r => r.success).length;
112
+ const failed = results.filter(r => !r.success).length;
113
+
114
+ logger.info(`Build complete: ${successful} successful, ${failed} failed`);
115
+
116
+ if (failed > 0) {
117
+ logger.warn('Some files failed to process:');
118
+ results.filter(r => !r.success).forEach(r => {
119
+ logger.warn(` - ${r.file}: ${r.error?.message}`);
120
+ });
121
+ }
122
+
123
+ // Generate index.ts file with all component exports
124
+ if (successful > 0) {
125
+ await this.generateIndexFile(outDir, results.filter(r => r.success).map(r => r.file));
126
+ }
127
+ }
128
+
129
+ /**
130
+ * Generate a React component from a single SVG file
131
+ */
132
+ public async generateSingle(options: GenerateOptions): Promise<void> {
133
+ logger.info(`Generating component from: ${options.svgFile}`);
134
+
135
+ const filePath = path.resolve(options.svgFile);
136
+ const outDir = path.resolve(options.outDir);
137
+
138
+ // Validate SVG file
139
+ if (!(await FileSystem.exists(filePath))) {
140
+ throw new Error(`SVG file not found: ${filePath}`);
141
+ }
142
+
143
+ // Check if file is locked
144
+ if (this.lockService.isLocked(filePath)) {
145
+ logger.warn(`File is locked: ${path.basename(options.svgFile)}`);
146
+ return;
147
+ }
148
+
149
+ // Get configuration
150
+ const config = configService.readConfig();
151
+ const mergedConfig = { ...config, ...options.config };
152
+
153
+ // Process the file
154
+ const result = await svgProcessor.processSVGFile(filePath, outDir, {
155
+ defaultWidth: mergedConfig.defaultWidth,
156
+ defaultHeight: mergedConfig.defaultHeight,
157
+ defaultFill: mergedConfig.defaultFill,
158
+ styleRules: Object.fromEntries(
159
+ Object.entries(mergedConfig.styleRules || {}).filter(([_, v]) => v !== undefined)
160
+ ) as Record<string, string>
161
+ });
162
+
163
+ if (!result.success) {
164
+ throw result.error || new Error('Failed to generate component');
165
+ }
166
+
167
+ logger.success(`Component generated: ${result.componentName}`);
168
+ }
169
+
170
+ /**
171
+ * Start watching SVG files for changes
172
+ */
173
+ public async startWatching(options: WatchOptions): Promise<string> {
174
+ logger.info('Starting watch mode');
175
+ logger.info(`Watching: ${options.src}`);
176
+ logger.info(`Output: ${options.out}`);
177
+
178
+ const srcDir = path.resolve(options.src);
179
+ const outDir = path.resolve(options.out);
180
+
181
+ // Validate source directory
182
+ if (!(await FileSystem.exists(srcDir))) {
183
+ throw new Error(`Source folder not found: ${srcDir}`);
184
+ }
185
+
186
+ // Start watching
187
+ const watchId = await fileWatcher.watchDirectory(srcDir, options);
188
+ this.activeWatchers.add(watchId);
189
+
190
+ // Register event handler
191
+ fileWatcher.onFileEvent(watchId, async (event: FileWatchEvent) => {
192
+ await this.handleWatchEvent(event, outDir, options.config);
193
+ });
194
+
195
+ logger.success(`Watch mode active - waiting for file changes...`);
196
+ return watchId;
197
+ }
198
+
199
+ /**
200
+ * Handle file watch events
201
+ */
202
+ private async handleWatchEvent(
203
+ event: FileWatchEvent,
204
+ outDir: string,
205
+ config?: Partial<any>
206
+ ): Promise<void> {
207
+ const fileName = path.basename(event.filePath);
208
+
209
+ switch (event.type) {
210
+ case 'add':
211
+ logger.info(`New SVG detected: ${fileName}`);
212
+ await this.processWatchedFile(event.filePath, outDir, config);
213
+ break;
214
+
215
+ case 'change':
216
+ logger.info(`SVG updated: ${fileName}`);
217
+ await this.processWatchedFile(event.filePath, outDir, config);
218
+ break;
219
+
220
+ case 'unlink':
221
+ logger.info(`SVG removed: ${fileName}`);
222
+ await this.handleFileRemoval(event.filePath, outDir);
223
+ break;
224
+ }
225
+ }
226
+
227
+ /**
228
+ * Process a watched file
229
+ */
230
+ private async processWatchedFile(
231
+ filePath: string,
232
+ outDir: string,
233
+ config?: Partial<any>
234
+ ): Promise<void> {
235
+ try {
236
+ // Check if file is locked
237
+ if (this.lockService.isLocked(filePath)) {
238
+ logger.warn(`Skipped locked file: ${path.basename(filePath)}`);
239
+ return;
240
+ }
241
+
242
+ // Get configuration
243
+ const fullConfig = configService.readConfig();
244
+ const mergedConfig = { ...fullConfig, ...config };
245
+
246
+ // Process the file
247
+ await svgProcessor.processSVGFile(filePath, outDir, {
248
+ defaultWidth: mergedConfig.defaultWidth,
249
+ defaultHeight: mergedConfig.defaultHeight,
250
+ defaultFill: mergedConfig.defaultFill,
251
+ styleRules: Object.fromEntries(
252
+ Object.entries(mergedConfig.styleRules || {}).filter(([_, v]) => v !== undefined)
253
+ ) as Record<string, string>
254
+ });
255
+
256
+ } catch (error) {
257
+ logger.error(`Failed to process watched file ${path.basename(filePath)}:`, error);
258
+ }
259
+ }
260
+
261
+ /**
262
+ * Handle file removal in watch mode
263
+ */
264
+ private async handleFileRemoval(filePath: string, outDir: string): Promise<void> {
265
+ try {
266
+ const componentName = svgProcessor.generateComponentName(path.basename(filePath));
267
+ const componentPath = path.join(outDir, `${componentName}.tsx`);
268
+
269
+ if (await FileSystem.exists(componentPath)) {
270
+ await FileSystem.unlink(componentPath);
271
+ logger.success(`Removed component: ${componentName}.tsx`);
272
+ }
273
+ } catch (error) {
274
+ logger.error(`Failed to remove component for ${path.basename(filePath)}:`, error);
275
+ }
276
+ }
277
+
278
+ /**
279
+ * Stop watching files
280
+ */
281
+ public stopWatching(watchId?: string): void {
282
+ if (watchId) {
283
+ fileWatcher.stopWatching(watchId);
284
+ this.activeWatchers.delete(watchId);
285
+ } else {
286
+ // Stop all watchers
287
+ for (const id of this.activeWatchers) {
288
+ fileWatcher.stopWatching(id);
289
+ }
290
+ this.activeWatchers.clear();
291
+ }
292
+ }
293
+
294
+ /**
295
+ * Clean output directory
296
+ */
297
+ public async clean(outDir: string): Promise<void> {
298
+ const targetDir = path.resolve(outDir);
299
+
300
+ if (!(await FileSystem.exists(targetDir))) {
301
+ logger.warn(`Directory not found: ${targetDir}`);
302
+ return;
303
+ }
304
+
305
+ await FileSystem.emptyDir(targetDir);
306
+ logger.success(`Cleaned all generated SVG components in: ${targetDir}`);
307
+ }
308
+
309
+ /**
310
+ * Generate index.ts file with all component exports
311
+ */
312
+ private async generateIndexFile(outDir: string, svgFiles: string[]): Promise<void> {
313
+ try {
314
+ const componentNames = svgFiles.map(file => {
315
+ const baseName = path.basename(file, '.svg');
316
+ return svgProcessor.generateComponentName(baseName);
317
+ });
318
+
319
+ const indexContent = this.generateIndexContent(componentNames);
320
+ const indexPath = path.join(outDir, 'index.ts');
321
+
322
+ await FileSystem.writeFile(indexPath, indexContent, 'utf-8');
323
+ logger.success(`Generated index.ts with ${componentNames.length} component exports`);
324
+ } catch (error) {
325
+ logger.error('Failed to generate index.ts:', error);
326
+ }
327
+ }
328
+
329
+ /**
330
+ * Generate the content for index.ts file
331
+ */
332
+ private generateIndexContent(componentNames: string[]): string {
333
+ const imports = componentNames.map(name => `export { default as ${name} } from './${name}';`).join('\n');
334
+
335
+ const exportAll = `
336
+ // Export all components
337
+ export {
338
+ ${componentNames.map(name => ` ${name},`).join('\n')}
339
+ };
340
+
341
+ // Re-export for convenience
342
+ export default {
343
+ ${componentNames.map(name => ` ${name},`).join('\n')}
344
+ };
345
+ `;
346
+
347
+ return `/**
348
+ * SVG Components Index
349
+ * Generated by svger-cli
350
+ *
351
+ * Import individual components:
352
+ * import { ${componentNames[0] || 'ComponentName'} } from './components';
353
+ *
354
+ * Import all components:
355
+ * import * as Icons from './components';
356
+ * import Icons from './components'; // default export
357
+ */
358
+
359
+ ${imports}${exportAll}`;
360
+ }
361
+
362
+ /**
363
+ * Get service statistics
364
+ */
365
+ public getStats(): {
366
+ activeWatchers: number;
367
+ processingQueue: any;
368
+ watcherStats: any;
369
+ } {
370
+ return {
371
+ activeWatchers: this.activeWatchers.size,
372
+ processingQueue: svgProcessor.getProcessingStats(),
373
+ watcherStats: fileWatcher.getWatchStats()
374
+ };
375
+ }
376
+
377
+ /**
378
+ * Shutdown service
379
+ */
380
+ public shutdown(): void {
381
+ this.stopWatching();
382
+ fileWatcher.shutdown();
383
+ svgProcessor.clearQueue();
384
+ logger.info('SVG service shutdown complete');
385
+ }
386
+ }
387
+
388
+ /**
389
+ * Simple file locking service
390
+ */
391
+ export class LockService {
392
+ private static instance: LockService;
393
+ private static readonly LOCK_FILE = '.svg-lock';
394
+ private cachedLocks: Set<string> | null = null;
395
+
396
+ private constructor() {}
397
+
398
+ public static getInstance(): LockService {
399
+ if (!LockService.instance) {
400
+ LockService.instance = new LockService();
401
+ }
402
+ return LockService.instance;
403
+ }
404
+
405
+ private getLockFilePath(): string {
406
+ return path.resolve(LockService.LOCK_FILE);
407
+ }
408
+
409
+ private readLockFile(): Set<string> {
410
+ if (this.cachedLocks) {
411
+ return this.cachedLocks;
412
+ }
413
+
414
+ try {
415
+ const data = FileSystem.readJSONSync(this.getLockFilePath());
416
+ this.cachedLocks = new Set(Array.isArray(data) ? data : []);
417
+ return this.cachedLocks;
418
+ } catch {
419
+ this.cachedLocks = new Set();
420
+ return this.cachedLocks;
421
+ }
422
+ }
423
+
424
+ private writeLockFile(locks: Set<string>): void {
425
+ try {
426
+ FileSystem.writeJSONSync(this.getLockFilePath(), Array.from(locks), { spaces: 2 });
427
+ this.cachedLocks = locks;
428
+ } catch (error) {
429
+ logger.error('Failed to write lock file:', error);
430
+ }
431
+ }
432
+
433
+ public lockFiles(files: string[]): void {
434
+ const fileNames = files.map(f => path.basename(f));
435
+ const current = this.readLockFile();
436
+
437
+ for (const fileName of fileNames) {
438
+ current.add(fileName);
439
+ }
440
+
441
+ this.writeLockFile(current);
442
+ logger.success(`Locked files: ${fileNames.join(', ')}`);
443
+ }
444
+
445
+ public unlockFiles(files: string[]): void {
446
+ const fileNames = files.map(f => path.basename(f));
447
+ const current = this.readLockFile();
448
+
449
+ for (const fileName of fileNames) {
450
+ current.delete(fileName);
451
+ }
452
+
453
+ this.writeLockFile(current);
454
+ logger.success(`Unlocked files: ${fileNames.join(', ')}`);
455
+ }
456
+
457
+ public isLocked(file: string): boolean {
458
+ const locks = this.readLockFile();
459
+ return locks.has(path.basename(file));
460
+ }
461
+
462
+ public clearCache(): void {
463
+ this.cachedLocks = null;
464
+ }
465
+ }
466
+
467
+ // Export singleton instance
468
+ export const svgService = SVGService.getInstance();
@@ -0,0 +1,169 @@
1
+ /**
2
+ * Type definitions for svger-cli
3
+ */
4
+
5
+ export type FrameworkType = 'react' | 'vue' | 'svelte' | 'angular' | 'solid' | 'preact' | 'lit' | 'vanilla';
6
+
7
+ export interface SVGConfig {
8
+ source: string;
9
+ output: string;
10
+ watch: boolean;
11
+ framework: FrameworkType;
12
+ typescript: boolean;
13
+ defaultWidth: number;
14
+ defaultHeight: number;
15
+ defaultFill: string;
16
+ exclude: string[];
17
+ styleRules: {
18
+ fill?: string;
19
+ stroke?: string;
20
+ [key: string]: string | undefined;
21
+ };
22
+ plugins?: PluginConfig[];
23
+ template?: TemplateConfig;
24
+ frameworkOptions?: FrameworkOptions;
25
+ errorHandling?: {
26
+ skipOnError: boolean;
27
+ logLevel: 'debug' | 'info' | 'warn' | 'error';
28
+ maxRetries: number;
29
+ };
30
+ performance?: {
31
+ batchSize: number;
32
+ parallel: boolean;
33
+ timeout: number;
34
+ enableCache: boolean;
35
+ };
36
+ }
37
+
38
+ export interface FrameworkOptions {
39
+ // Vue-specific
40
+ composition?: boolean;
41
+ setup?: boolean;
42
+ scriptSetup?: boolean;
43
+
44
+ // Angular-specific
45
+ standalone?: boolean;
46
+ moduleImport?: boolean;
47
+
48
+ // Solid-specific
49
+ signals?: boolean;
50
+
51
+ // React/Preact-specific
52
+ forwardRef?: boolean;
53
+ memo?: boolean;
54
+
55
+ // Lit-specific
56
+ customElement?: boolean;
57
+ shadowDom?: boolean;
58
+
59
+ // General
60
+ cssModules?: boolean;
61
+ styledComponents?: boolean;
62
+ }
63
+
64
+ export interface BuildOptions {
65
+ src: string;
66
+ out: string;
67
+ config?: Partial<SVGConfig>;
68
+ }
69
+
70
+ export interface GenerateOptions {
71
+ svgFile: string;
72
+ outDir: string;
73
+ config?: Partial<SVGConfig>;
74
+ }
75
+
76
+ export interface WatchOptions {
77
+ src: string;
78
+ out: string;
79
+ config?: Partial<SVGConfig>;
80
+ }
81
+
82
+ export interface ComponentGenerationOptions {
83
+ componentName: string;
84
+ svgContent: string;
85
+ framework: FrameworkType;
86
+ typescript: boolean;
87
+ defaultWidth?: number;
88
+ defaultHeight?: number;
89
+ defaultFill?: string;
90
+ styleRules?: Record<string, string>;
91
+ template?: TemplateConfig;
92
+ frameworkOptions?: FrameworkOptions;
93
+ }
94
+
95
+ export interface TemplateConfig {
96
+ type: 'default' | 'custom';
97
+ path?: string;
98
+ options?: Record<string, any>;
99
+ }
100
+
101
+ export interface PluginConfig {
102
+ name: string;
103
+ options?: Record<string, any>;
104
+ }
105
+
106
+ export interface SVGProcessorResult {
107
+ success: boolean;
108
+ componentName: string;
109
+ filePath: string;
110
+ error?: Error;
111
+ }
112
+
113
+ export interface FileWatchEvent {
114
+ type: 'add' | 'change' | 'unlink';
115
+ filePath: string;
116
+ timestamp: number;
117
+ }
118
+
119
+ export interface ProcessingContext {
120
+ config: SVGConfig;
121
+ sourceDir: string;
122
+ outputDir: string;
123
+ fileQueue: string[];
124
+ locks: Set<string>;
125
+ cache?: Map<string, CachedComponent>;
126
+ }
127
+
128
+ export interface CachedComponent {
129
+ hash: string;
130
+ componentName: string;
131
+ filePath: string;
132
+ framework: FrameworkType;
133
+ timestamp: number;
134
+ svgHash: string;
135
+ }
136
+
137
+ export interface Logger {
138
+ info(message: string, ...args: any[]): void;
139
+ warn(message: string, ...args: any[]): void;
140
+ error(message: string, ...args: any[]): void;
141
+ success(message: string, ...args: any[]): void;
142
+ debug(message: string, ...args: any[]): void;
143
+ }
144
+
145
+ export interface Plugin {
146
+ name: string;
147
+ version: string;
148
+ process(content: string, options?: any): Promise<string>;
149
+ validate?(options?: any): boolean;
150
+ }
151
+
152
+ export interface Template {
153
+ name: string;
154
+ generate(options: ComponentGenerationOptions): string;
155
+ validate?(options: ComponentGenerationOptions): boolean;
156
+ }
157
+
158
+ export type FileSystemEvent = 'change' | 'rename';
159
+ export type LogLevel = 'debug' | 'info' | 'warn' | 'error';
160
+ export type ProcessingStatus = 'pending' | 'processing' | 'completed' | 'failed';
161
+
162
+ export interface ProcessingJob {
163
+ id: string;
164
+ filePath: string;
165
+ status: ProcessingStatus;
166
+ startTime: number;
167
+ endTime?: number;
168
+ error?: Error;
169
+ }