svger-cli 2.0.1 → 2.0.2
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/cli.js +0 -0
- package/dist/core/error-handler.d.ts +63 -0
- package/dist/core/error-handler.js +224 -0
- package/dist/core/framework-templates.d.ts +17 -0
- package/{src/core/framework-templates.ts → dist/core/framework-templates.js} +100 -137
- package/dist/core/logger.d.ts +22 -0
- package/dist/core/logger.js +85 -0
- package/dist/core/performance-engine.d.ts +67 -0
- package/dist/core/performance-engine.js +251 -0
- package/dist/core/plugin-manager.d.ts +56 -0
- package/dist/core/plugin-manager.js +189 -0
- package/dist/core/style-compiler.d.ts +88 -0
- package/dist/core/style-compiler.js +466 -0
- package/dist/core/template-manager.d.ts +64 -0
- package/{src/core/template-manager.ts → dist/core/template-manager.js} +172 -255
- package/dist/index.d.ts +151 -0
- package/{src/index.ts → dist/index.js} +30 -108
- package/dist/processors/svg-processor.d.ts +67 -0
- package/dist/processors/svg-processor.js +225 -0
- package/dist/services/config.d.ts +55 -0
- package/dist/services/config.js +209 -0
- package/dist/services/file-watcher.d.ts +54 -0
- package/dist/services/file-watcher.js +180 -0
- package/dist/services/svg-service.d.ts +81 -0
- package/dist/services/svg-service.js +383 -0
- package/dist/types/index.d.ts +140 -0
- package/dist/types/index.js +4 -0
- package/dist/utils/native.d.ts +74 -0
- package/dist/utils/native.js +305 -0
- package/package.json +9 -10
- package/.svgconfig.json +0 -3
- package/CODE_OF_CONDUCT.md +0 -79
- package/CONTRIBUTING.md +0 -146
- package/TESTING.md +0 -143
- package/cli-framework.test.js +0 -16
- package/cli-test-angular/Arrowbenddownleft.component.ts +0 -27
- package/cli-test-angular/Vite.component.ts +0 -27
- package/cli-test-angular/index.ts +0 -25
- package/cli-test-output/Arrowbenddownleft.vue +0 -33
- package/cli-test-output/Vite.vue +0 -33
- package/cli-test-output/index.ts +0 -25
- package/cli-test-react/Arrowbenddownleft.tsx +0 -39
- package/cli-test-react/Vite.tsx +0 -39
- package/cli-test-react/index.ts +0 -25
- package/cli-test-svelte/Arrowbenddownleft.svelte +0 -22
- package/cli-test-svelte/Vite.svelte +0 -22
- package/cli-test-svelte/index.ts +0 -25
- package/docs/ADR-SVG-INTRGRATION-METHODS-001.adr.md +0 -157
- package/docs/ADR-SVG-INTRGRATION-METHODS-002.adr.md +0 -550
- package/docs/FRAMEWORK-GUIDE.md +0 -768
- package/docs/IMPLEMENTATION-SUMMARY.md +0 -376
- package/docs/TDR-SVG-INTRGRATION-METHODS-001.tdr.md +0 -115
- package/frameworks.test.js +0 -170
- package/my-svgs/ArrowBendDownLeft.svg +0 -6
- package/my-svgs/vite.svg +0 -1
- package/src/builder.ts +0 -104
- package/src/clean.ts +0 -21
- package/src/cli.ts +0 -221
- package/src/config.ts +0 -81
- package/src/core/error-handler.ts +0 -303
- package/src/core/logger.ts +0 -104
- package/src/core/performance-engine.ts +0 -327
- package/src/core/plugin-manager.ts +0 -228
- package/src/core/style-compiler.ts +0 -605
- package/src/lock.ts +0 -74
- package/src/processors/svg-processor.ts +0 -288
- package/src/services/config.ts +0 -241
- package/src/services/file-watcher.ts +0 -218
- package/src/services/svg-service.ts +0 -468
- package/src/templates/ComponentTemplate.ts +0 -57
- package/src/types/index.ts +0 -169
- package/src/utils/native.ts +0 -352
- package/src/watch.ts +0 -88
- package/test-output-mulit/TestIcon-angular-module.component.ts +0 -26
- package/test-output-mulit/TestIcon-angular-standalone.component.ts +0 -27
- package/test-output-mulit/TestIcon-lit.ts +0 -35
- package/test-output-mulit/TestIcon-preact.tsx +0 -38
- package/test-output-mulit/TestIcon-react.tsx +0 -35
- package/test-output-mulit/TestIcon-solid.tsx +0 -27
- package/test-output-mulit/TestIcon-svelte.svelte +0 -22
- package/test-output-mulit/TestIcon-vanilla.ts +0 -37
- package/test-output-mulit/TestIcon-vue-composition.vue +0 -33
- package/test-output-mulit/TestIcon-vue-options.vue +0 -31
- package/tsconfig.json +0 -18
|
@@ -1,288 +0,0 @@
|
|
|
1
|
-
import path from 'path';
|
|
2
|
-
import { toPascalCase, FileSystem } from '../utils/native.js';
|
|
3
|
-
import { ComponentGenerationOptions, SVGProcessorResult, ProcessingJob, ProcessingStatus } from '../types/index.js';
|
|
4
|
-
import { logger } from '../core/logger.js';
|
|
5
|
-
import { pluginManager } from '../core/plugin-manager.js';
|
|
6
|
-
import { templateManager } from '../core/template-manager.js';
|
|
7
|
-
import { performanceEngine } from '../core/performance-engine.js';
|
|
8
|
-
import { frameworkTemplateEngine } from '../core/framework-templates.js';
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* SVG content processor and component generator
|
|
12
|
-
*/
|
|
13
|
-
export class SVGProcessor {
|
|
14
|
-
private static instance: SVGProcessor;
|
|
15
|
-
private processingQueue: Map<string, ProcessingJob> = new Map();
|
|
16
|
-
private jobCounter = 0;
|
|
17
|
-
|
|
18
|
-
private constructor() {}
|
|
19
|
-
|
|
20
|
-
public static getInstance(): SVGProcessor {
|
|
21
|
-
if (!SVGProcessor.instance) {
|
|
22
|
-
SVGProcessor.instance = new SVGProcessor();
|
|
23
|
-
}
|
|
24
|
-
return SVGProcessor.instance;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Clean and optimize SVG content
|
|
29
|
-
*/
|
|
30
|
-
public cleanSVGContent(svgContent: string): string {
|
|
31
|
-
logger.debug('Cleaning SVG content');
|
|
32
|
-
|
|
33
|
-
return svgContent
|
|
34
|
-
// Remove XML declaration
|
|
35
|
-
.replace(/<\?xml.*?\?>/g, '')
|
|
36
|
-
// Remove DOCTYPE declaration
|
|
37
|
-
.replace(/<!DOCTYPE.*?>/g, '')
|
|
38
|
-
// Remove comments
|
|
39
|
-
.replace(/<!--[\s\S]*?-->/g, '')
|
|
40
|
-
// Normalize whitespace
|
|
41
|
-
.replace(/\r?\n|\r/g, '')
|
|
42
|
-
.replace(/\s{2,}/g, ' ')
|
|
43
|
-
// Remove inline styles (they interfere with React styling)
|
|
44
|
-
.replace(/style="[^"]*"/g, '')
|
|
45
|
-
// Remove xmlns attributes (React will handle these)
|
|
46
|
-
.replace(/\s+xmlns(:xlink)?="[^"]*"/g, '')
|
|
47
|
-
// Convert attributes to camelCase for React
|
|
48
|
-
.replace(/fill-rule/g, 'fillRule')
|
|
49
|
-
.replace(/clip-rule/g, 'clipRule')
|
|
50
|
-
.replace(/stroke-width/g, 'strokeWidth')
|
|
51
|
-
.replace(/stroke-linecap/g, 'strokeLinecap')
|
|
52
|
-
.replace(/stroke-linejoin/g, 'strokeLinejoin')
|
|
53
|
-
.replace(/stroke-miterlimit/g, 'strokeMiterlimit')
|
|
54
|
-
.replace(/stroke-dasharray/g, 'strokeDasharray')
|
|
55
|
-
.replace(/stroke-dashoffset/g, 'strokeDashoffset')
|
|
56
|
-
.replace(/font-family/g, 'fontFamily')
|
|
57
|
-
.replace(/font-size/g, 'fontSize')
|
|
58
|
-
.replace(/font-weight/g, 'fontWeight')
|
|
59
|
-
.replace(/text-anchor/g, 'textAnchor')
|
|
60
|
-
// Remove outer SVG tag and keep inner content
|
|
61
|
-
.trim()
|
|
62
|
-
.replace(/^<svg[^>]*>([\s\S]*)<\/svg>$/i, '$1')
|
|
63
|
-
.trim();
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Extract viewBox from SVG content
|
|
68
|
-
*/
|
|
69
|
-
public extractViewBox(svgContent: string): string | null {
|
|
70
|
-
const viewBoxMatch = svgContent.match(/viewBox=["']([^"']+)["']/i);
|
|
71
|
-
return viewBoxMatch ? viewBoxMatch[1] : null;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Generate component name from filename
|
|
76
|
-
*/
|
|
77
|
-
public generateComponentName(fileName: string): string {
|
|
78
|
-
const baseName = path.basename(fileName, '.svg');
|
|
79
|
-
const componentName = toPascalCase(baseName);
|
|
80
|
-
|
|
81
|
-
// Ensure component name starts with uppercase letter
|
|
82
|
-
if (!/^[A-Z]/.test(componentName)) {
|
|
83
|
-
return `Svg${componentName}`;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
return componentName;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* Generate React component from SVG content
|
|
91
|
-
*/
|
|
92
|
-
public async generateComponent(
|
|
93
|
-
componentName: string,
|
|
94
|
-
svgContent: string,
|
|
95
|
-
options: Partial<ComponentGenerationOptions> = {}
|
|
96
|
-
): Promise<string> {
|
|
97
|
-
try {
|
|
98
|
-
// Clean and optimize SVG content
|
|
99
|
-
const cleanedContent = this.cleanSVGContent(svgContent);
|
|
100
|
-
|
|
101
|
-
// Apply plugins (no plugin configs for now, just process directly)
|
|
102
|
-
const processedContent = cleanedContent;
|
|
103
|
-
|
|
104
|
-
// Create full options object with required fields
|
|
105
|
-
const fullOptions: ComponentGenerationOptions = {
|
|
106
|
-
componentName,
|
|
107
|
-
svgContent: processedContent,
|
|
108
|
-
framework: options.framework || 'react',
|
|
109
|
-
typescript: options.typescript !== undefined ? options.typescript : true,
|
|
110
|
-
...options
|
|
111
|
-
};
|
|
112
|
-
|
|
113
|
-
// Use framework template engine directly
|
|
114
|
-
const component = frameworkTemplateEngine.generateComponent(fullOptions);
|
|
115
|
-
|
|
116
|
-
logger.debug(`Generated component: ${componentName}`);
|
|
117
|
-
return component;
|
|
118
|
-
|
|
119
|
-
} catch (error) {
|
|
120
|
-
logger.error(`Failed to generate component ${componentName}:`, error);
|
|
121
|
-
throw error;
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
/**
|
|
126
|
-
* Generate framework-agnostic component from SVG content
|
|
127
|
-
*/
|
|
128
|
-
public async generateFrameworkComponent(
|
|
129
|
-
componentName: string,
|
|
130
|
-
svgContent: string,
|
|
131
|
-
options: ComponentGenerationOptions
|
|
132
|
-
): Promise<string> {
|
|
133
|
-
try {
|
|
134
|
-
// Optimize SVG content based on framework requirements
|
|
135
|
-
const optimizationLevel = options.framework === 'vanilla' ? 'maximum' : 'balanced';
|
|
136
|
-
const optimizedContent = performanceEngine.optimizeSVGContent(svgContent, optimizationLevel);
|
|
137
|
-
|
|
138
|
-
// Generate framework-specific component
|
|
139
|
-
const component = frameworkTemplateEngine.generateComponent({
|
|
140
|
-
...options,
|
|
141
|
-
componentName,
|
|
142
|
-
svgContent: optimizedContent
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
logger.debug(`Generated ${options.framework} component: ${componentName}`);
|
|
146
|
-
return component;
|
|
147
|
-
|
|
148
|
-
} catch (error) {
|
|
149
|
-
logger.error(`Failed to generate ${options.framework} component ${componentName}:`, error);
|
|
150
|
-
throw error;
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
/**
|
|
155
|
-
* Process multiple SVG files in batch with performance optimization
|
|
156
|
-
*/
|
|
157
|
-
public async processBatch(
|
|
158
|
-
files: Array<{ path: string; outputDir: string; options?: Partial<ComponentGenerationOptions> }>,
|
|
159
|
-
config: { batchSize?: number; parallel?: boolean; maxConcurrency?: number } = {}
|
|
160
|
-
): Promise<Array<{ success: boolean; filePath: string; error?: Error; duration: number }>> {
|
|
161
|
-
logger.info(`Starting batch processing of ${files.length} files`);
|
|
162
|
-
|
|
163
|
-
try {
|
|
164
|
-
const results = await performanceEngine.processBatch(files, config);
|
|
165
|
-
|
|
166
|
-
// Log performance metrics
|
|
167
|
-
const metrics = performanceEngine.getPerformanceMetrics();
|
|
168
|
-
if (metrics.memoryUsage.recommendations.length > 0) {
|
|
169
|
-
logger.warn('Performance recommendations:', metrics.memoryUsage.recommendations);
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
return results;
|
|
173
|
-
} catch (error) {
|
|
174
|
-
logger.error('Batch processing failed:', error);
|
|
175
|
-
throw error;
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
/**
|
|
180
|
-
* Process a single SVG file
|
|
181
|
-
*/
|
|
182
|
-
public async processSVGFile(
|
|
183
|
-
svgFilePath: string,
|
|
184
|
-
outputDir: string,
|
|
185
|
-
options: Partial<ComponentGenerationOptions> = {}
|
|
186
|
-
): Promise<SVGProcessorResult> {
|
|
187
|
-
const jobId = `job-${++this.jobCounter}`;
|
|
188
|
-
const job: ProcessingJob = {
|
|
189
|
-
id: jobId,
|
|
190
|
-
filePath: svgFilePath,
|
|
191
|
-
status: 'processing',
|
|
192
|
-
startTime: Date.now()
|
|
193
|
-
};
|
|
194
|
-
|
|
195
|
-
this.processingQueue.set(jobId, job);
|
|
196
|
-
logger.debug(`Processing SVG file: ${svgFilePath}`);
|
|
197
|
-
|
|
198
|
-
try {
|
|
199
|
-
// Read SVG content
|
|
200
|
-
const svgContent = await FileSystem.readFile(svgFilePath, 'utf-8');
|
|
201
|
-
|
|
202
|
-
// Generate component name
|
|
203
|
-
const componentName = this.generateComponentName(path.basename(svgFilePath));
|
|
204
|
-
|
|
205
|
-
// Generate component code
|
|
206
|
-
const componentCode = await this.generateComponent(
|
|
207
|
-
componentName,
|
|
208
|
-
svgContent,
|
|
209
|
-
options
|
|
210
|
-
);
|
|
211
|
-
|
|
212
|
-
// Ensure output directory exists
|
|
213
|
-
await FileSystem.ensureDir(outputDir);
|
|
214
|
-
|
|
215
|
-
// Get correct file extension based on framework
|
|
216
|
-
const framework = options.framework || 'react';
|
|
217
|
-
const typescript = options.typescript !== undefined ? options.typescript : true;
|
|
218
|
-
const fileExtension = frameworkTemplateEngine.getFileExtension(framework, typescript);
|
|
219
|
-
|
|
220
|
-
// Write component file
|
|
221
|
-
const outputFilePath = path.join(outputDir, `${componentName}.${fileExtension}`);
|
|
222
|
-
await FileSystem.writeFile(outputFilePath, componentCode, 'utf-8');
|
|
223
|
-
|
|
224
|
-
// Update job status
|
|
225
|
-
job.status = 'completed';
|
|
226
|
-
job.endTime = Date.now();
|
|
227
|
-
|
|
228
|
-
const result: SVGProcessorResult = {
|
|
229
|
-
success: true,
|
|
230
|
-
componentName,
|
|
231
|
-
filePath: outputFilePath
|
|
232
|
-
};
|
|
233
|
-
|
|
234
|
-
logger.success(`Generated component: ${componentName}.${fileExtension}`);
|
|
235
|
-
return result;
|
|
236
|
-
|
|
237
|
-
} catch (error) {
|
|
238
|
-
job.status = 'failed';
|
|
239
|
-
job.endTime = Date.now();
|
|
240
|
-
job.error = error as Error;
|
|
241
|
-
|
|
242
|
-
const result: SVGProcessorResult = {
|
|
243
|
-
success: false,
|
|
244
|
-
componentName: '',
|
|
245
|
-
filePath: svgFilePath,
|
|
246
|
-
error: error as Error
|
|
247
|
-
};
|
|
248
|
-
|
|
249
|
-
logger.error(`Failed to process ${svgFilePath}:`, error);
|
|
250
|
-
return result;
|
|
251
|
-
} finally {
|
|
252
|
-
// Clean up completed jobs after some time
|
|
253
|
-
setTimeout(() => {
|
|
254
|
-
this.processingQueue.delete(jobId);
|
|
255
|
-
}, 30000); // 30 seconds
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
/**
|
|
260
|
-
* Get processing statistics
|
|
261
|
-
*/
|
|
262
|
-
public getProcessingStats(): {
|
|
263
|
-
total: number;
|
|
264
|
-
pending: number;
|
|
265
|
-
processing: number;
|
|
266
|
-
completed: number;
|
|
267
|
-
failed: number;
|
|
268
|
-
} {
|
|
269
|
-
const jobs = Array.from(this.processingQueue.values());
|
|
270
|
-
return {
|
|
271
|
-
total: jobs.length,
|
|
272
|
-
pending: jobs.filter(j => j.status === 'pending').length,
|
|
273
|
-
processing: jobs.filter(j => j.status === 'processing').length,
|
|
274
|
-
completed: jobs.filter(j => j.status === 'completed').length,
|
|
275
|
-
failed: jobs.filter(j => j.status === 'failed').length,
|
|
276
|
-
};
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
/**
|
|
280
|
-
* Clear processing queue
|
|
281
|
-
*/
|
|
282
|
-
public clearQueue(): void {
|
|
283
|
-
this.processingQueue.clear();
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
// Export singleton instance
|
|
288
|
-
export const svgProcessor = SVGProcessor.getInstance();
|
package/src/services/config.ts
DELETED
|
@@ -1,241 +0,0 @@
|
|
|
1
|
-
import path from 'path';
|
|
2
|
-
import { FileSystem } from '../utils/native.js';
|
|
3
|
-
import { SVGConfig } from '../types/index.js';
|
|
4
|
-
import { logger } from '../core/logger.js';
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Professional configuration management service
|
|
8
|
-
*/
|
|
9
|
-
export class ConfigService {
|
|
10
|
-
private static instance: ConfigService;
|
|
11
|
-
private static readonly CONFIG_FILE = '.svgconfig.json';
|
|
12
|
-
private cachedConfig: SVGConfig | null = null;
|
|
13
|
-
|
|
14
|
-
private constructor() {}
|
|
15
|
-
|
|
16
|
-
public static getInstance(): ConfigService {
|
|
17
|
-
if (!ConfigService.instance) {
|
|
18
|
-
ConfigService.instance = new ConfigService();
|
|
19
|
-
}
|
|
20
|
-
return ConfigService.instance;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Get the default configuration
|
|
25
|
-
*/
|
|
26
|
-
public getDefaultConfig(): SVGConfig {
|
|
27
|
-
return {
|
|
28
|
-
source: './src/assets/svg',
|
|
29
|
-
output: './src/components/icons',
|
|
30
|
-
framework: 'react',
|
|
31
|
-
typescript: true,
|
|
32
|
-
watch: false,
|
|
33
|
-
defaultWidth: 24,
|
|
34
|
-
defaultHeight: 24,
|
|
35
|
-
defaultFill: 'currentColor',
|
|
36
|
-
exclude: [],
|
|
37
|
-
styleRules: {
|
|
38
|
-
fill: 'inherit',
|
|
39
|
-
stroke: 'none',
|
|
40
|
-
},
|
|
41
|
-
plugins: [],
|
|
42
|
-
template: {
|
|
43
|
-
type: 'default'
|
|
44
|
-
},
|
|
45
|
-
frameworkOptions: {
|
|
46
|
-
forwardRef: true,
|
|
47
|
-
memo: false,
|
|
48
|
-
scriptSetup: true,
|
|
49
|
-
standalone: true
|
|
50
|
-
},
|
|
51
|
-
errorHandling: {
|
|
52
|
-
skipOnError: false,
|
|
53
|
-
logLevel: 'info',
|
|
54
|
-
maxRetries: 3
|
|
55
|
-
},
|
|
56
|
-
performance: {
|
|
57
|
-
batchSize: 10,
|
|
58
|
-
parallel: true,
|
|
59
|
-
timeout: 30000,
|
|
60
|
-
enableCache: true
|
|
61
|
-
}
|
|
62
|
-
};
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* Get configuration file path
|
|
67
|
-
*/
|
|
68
|
-
private getConfigPath(): string {
|
|
69
|
-
return path.resolve(ConfigService.CONFIG_FILE);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* Read configuration from file with caching
|
|
74
|
-
*/
|
|
75
|
-
public readConfig(): SVGConfig {
|
|
76
|
-
if (this.cachedConfig) {
|
|
77
|
-
return { ...this.cachedConfig };
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
try {
|
|
81
|
-
const configData = FileSystem.readJSONSync(this.getConfigPath());
|
|
82
|
-
|
|
83
|
-
if (Object.keys(configData).length === 0) {
|
|
84
|
-
logger.debug('No configuration found, using defaults');
|
|
85
|
-
this.cachedConfig = this.getDefaultConfig();
|
|
86
|
-
return this.cachedConfig;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
// Merge with defaults to ensure all required properties exist
|
|
90
|
-
this.cachedConfig = {
|
|
91
|
-
...this.getDefaultConfig(),
|
|
92
|
-
...configData
|
|
93
|
-
};
|
|
94
|
-
|
|
95
|
-
logger.debug('Configuration loaded successfully');
|
|
96
|
-
return this.cachedConfig!;
|
|
97
|
-
} catch (error) {
|
|
98
|
-
logger.warn('Failed to read configuration, using defaults:', error);
|
|
99
|
-
this.cachedConfig = this.getDefaultConfig();
|
|
100
|
-
return this.cachedConfig;
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* Write configuration to file
|
|
106
|
-
*/
|
|
107
|
-
public writeConfig(config: SVGConfig): void {
|
|
108
|
-
try {
|
|
109
|
-
FileSystem.writeJSONSync(this.getConfigPath(), config, { spaces: 2 });
|
|
110
|
-
this.cachedConfig = config; // Update cache
|
|
111
|
-
logger.success('Configuration saved successfully');
|
|
112
|
-
} catch (error) {
|
|
113
|
-
logger.error('Failed to write configuration:', error);
|
|
114
|
-
throw error;
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
/**
|
|
119
|
-
* Initialize configuration with defaults
|
|
120
|
-
*/
|
|
121
|
-
public async initConfig(): Promise<void> {
|
|
122
|
-
const configPath = this.getConfigPath();
|
|
123
|
-
|
|
124
|
-
if (await FileSystem.exists(configPath)) {
|
|
125
|
-
logger.warn('Config file already exists:', configPath);
|
|
126
|
-
return;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
const defaultConfig = this.getDefaultConfig();
|
|
130
|
-
this.writeConfig(defaultConfig);
|
|
131
|
-
logger.success('Configuration initialized with defaults');
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
/**
|
|
135
|
-
* Set a specific configuration value
|
|
136
|
-
*/
|
|
137
|
-
public setConfig(key: string, value: any): void {
|
|
138
|
-
const config = this.readConfig();
|
|
139
|
-
|
|
140
|
-
// Support nested key paths like 'styleRules.fill'
|
|
141
|
-
const keys = key.split('.');
|
|
142
|
-
let current: any = config;
|
|
143
|
-
|
|
144
|
-
for (let i = 0; i < keys.length - 1; i++) {
|
|
145
|
-
const k = keys[i];
|
|
146
|
-
if (!(k in current)) {
|
|
147
|
-
current[k] = {};
|
|
148
|
-
}
|
|
149
|
-
current = current[k];
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
const finalKey = keys[keys.length - 1];
|
|
153
|
-
current[finalKey] = value;
|
|
154
|
-
|
|
155
|
-
this.writeConfig(config);
|
|
156
|
-
logger.success(`Configuration updated: ${key} = ${value}`);
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
/**
|
|
160
|
-
* Get a specific configuration value
|
|
161
|
-
*/
|
|
162
|
-
public getConfig(key?: string): any {
|
|
163
|
-
const config = this.readConfig();
|
|
164
|
-
|
|
165
|
-
if (!key) {
|
|
166
|
-
return config;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
// Support nested key paths
|
|
170
|
-
const keys = key.split('.');
|
|
171
|
-
let current: any = config;
|
|
172
|
-
|
|
173
|
-
for (const k of keys) {
|
|
174
|
-
if (!(k in current)) {
|
|
175
|
-
return undefined;
|
|
176
|
-
}
|
|
177
|
-
current = current[k];
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
return current;
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
/**
|
|
184
|
-
* Validate configuration
|
|
185
|
-
*/
|
|
186
|
-
public validateConfig(config?: SVGConfig): { valid: boolean; errors: string[] } {
|
|
187
|
-
const configToValidate = config || this.readConfig();
|
|
188
|
-
const errors: string[] = [];
|
|
189
|
-
|
|
190
|
-
// Required string fields
|
|
191
|
-
const requiredStringFields = ['source', 'output', 'defaultFill'];
|
|
192
|
-
for (const field of requiredStringFields) {
|
|
193
|
-
if (!configToValidate[field as keyof SVGConfig] || typeof configToValidate[field as keyof SVGConfig] !== 'string') {
|
|
194
|
-
errors.push(`${field} must be a non-empty string`);
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
// Required numeric fields
|
|
199
|
-
const requiredNumericFields = ['defaultWidth', 'defaultHeight'];
|
|
200
|
-
for (const field of requiredNumericFields) {
|
|
201
|
-
const value = configToValidate[field as keyof SVGConfig];
|
|
202
|
-
if (typeof value !== 'number' || value <= 0) {
|
|
203
|
-
errors.push(`${field} must be a positive number`);
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
// Validate exclude array
|
|
208
|
-
if (configToValidate.exclude && !Array.isArray(configToValidate.exclude)) {
|
|
209
|
-
errors.push('exclude must be an array');
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
// Validate styleRules object
|
|
213
|
-
if (configToValidate.styleRules && typeof configToValidate.styleRules !== 'object') {
|
|
214
|
-
errors.push('styleRules must be an object');
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
return {
|
|
218
|
-
valid: errors.length === 0,
|
|
219
|
-
errors
|
|
220
|
-
};
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
/**
|
|
224
|
-
* Clear cached configuration (useful for testing)
|
|
225
|
-
*/
|
|
226
|
-
public clearCache(): void {
|
|
227
|
-
this.cachedConfig = null;
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
/**
|
|
231
|
-
* Display current configuration
|
|
232
|
-
*/
|
|
233
|
-
public showConfig(): void {
|
|
234
|
-
const config = this.readConfig();
|
|
235
|
-
console.log('📄 Current Configuration:');
|
|
236
|
-
console.log(JSON.stringify(config, null, 2));
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
// Export singleton instance
|
|
241
|
-
export const configService = ConfigService.getInstance();
|