svger-cli 4.0.2 → 4.0.3
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/CHANGELOG.md +114 -0
- package/README.md +27 -27
- package/dist/builder.d.ts +6 -3
- package/dist/builder.js +34 -24
- package/dist/cli.js +30 -24
- package/dist/config.d.ts +8 -2
- package/dist/config.js +17 -124
- package/dist/core/logger.js +8 -4
- package/dist/core/performance-engine.js +16 -3
- package/dist/core/style-compiler.js +6 -7
- package/dist/core/template-manager.js +18 -14
- package/dist/index.d.ts +1 -2
- package/dist/index.js +8 -2
- package/dist/integrations/jest-preset.js +30 -2
- package/dist/optimizers/path-parser.js +199 -115
- package/dist/optimizers/path-simplifier.js +27 -24
- package/dist/optimizers/shape-conversion.js +22 -27
- package/dist/optimizers/transform-collapsing.js +11 -15
- package/dist/optimizers/transform-optimizer.js +16 -21
- package/dist/optimizers/types.js +64 -74
- package/dist/processors/svg-processor.js +20 -20
- package/dist/services/config.js +8 -4
- package/dist/services/svg-service.js +9 -2
- package/dist/utils/native.d.ts +0 -1
- package/dist/utils/native.js +6 -14
- package/dist/watch.js +4 -3
- package/docs/ERROR-HANDLING-STANDARD.md +1 -1
- package/docs/OPTIONAL-DEPENDENCIES.md +1 -1
- package/package.json +1 -1
package/dist/optimizers/types.js
CHANGED
|
@@ -45,78 +45,68 @@ export function getDefaultOptConfig(level) {
|
|
|
45
45
|
shapeConversionThreshold: 5,
|
|
46
46
|
optimizationLevel: level,
|
|
47
47
|
};
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
enableTransformOptimization: true,
|
|
113
|
-
enableTransformCollapsing: true,
|
|
114
|
-
enablePathOptimization: true,
|
|
115
|
-
enablePathSimplification: true,
|
|
116
|
-
shapeConversion: true,
|
|
117
|
-
shapeConversionThreshold: 0, // Convert even 1-byte savings
|
|
118
|
-
};
|
|
119
|
-
default:
|
|
120
|
-
return baseConfig;
|
|
121
|
-
}
|
|
48
|
+
// O(1) object lookup for optimization level → config overrides
|
|
49
|
+
const levelOverrides = {
|
|
50
|
+
[OptLevel.NONE]: {
|
|
51
|
+
removeMetadata: false,
|
|
52
|
+
removeComments: false,
|
|
53
|
+
normalizeWhitespace: false,
|
|
54
|
+
removeUnnecessaryAttrs: false,
|
|
55
|
+
shortenColors: false,
|
|
56
|
+
removeEmptyContainers: false,
|
|
57
|
+
removeDoctype: false,
|
|
58
|
+
removeXMLProcInst: false,
|
|
59
|
+
enableNumericOptimization: false,
|
|
60
|
+
enableStyleOptimization: false,
|
|
61
|
+
enableTransformOptimization: false,
|
|
62
|
+
enableTransformCollapsing: false,
|
|
63
|
+
enablePathOptimization: false,
|
|
64
|
+
enablePathSimplification: false,
|
|
65
|
+
},
|
|
66
|
+
[OptLevel.BASIC]: {},
|
|
67
|
+
[OptLevel.BALANCED]: {
|
|
68
|
+
removeHiddenElements: true,
|
|
69
|
+
floatPrecision: 3,
|
|
70
|
+
enableNumericOptimization: true,
|
|
71
|
+
enableStyleOptimization: true,
|
|
72
|
+
enableTransformOptimization: false,
|
|
73
|
+
enableTransformCollapsing: false,
|
|
74
|
+
enablePathOptimization: false,
|
|
75
|
+
},
|
|
76
|
+
[OptLevel.AGGRESSIVE]: {
|
|
77
|
+
removeHiddenElements: true,
|
|
78
|
+
mergePaths: true,
|
|
79
|
+
collapseGroups: true,
|
|
80
|
+
floatPrecision: 2,
|
|
81
|
+
pathTolerance: 0.7,
|
|
82
|
+
sortAttrs: true,
|
|
83
|
+
enableNumericOptimization: true,
|
|
84
|
+
enableStyleOptimization: true,
|
|
85
|
+
enableTransformOptimization: true,
|
|
86
|
+
enableTransformCollapsing: true,
|
|
87
|
+
enablePathOptimization: true,
|
|
88
|
+
shapeConversion: false,
|
|
89
|
+
shapeConversionThreshold: 5,
|
|
90
|
+
},
|
|
91
|
+
[OptLevel.MAXIMUM]: {
|
|
92
|
+
removeHiddenElements: true,
|
|
93
|
+
mergePaths: true,
|
|
94
|
+
collapseGroups: true,
|
|
95
|
+
inlineStyles: true,
|
|
96
|
+
floatPrecision: 1,
|
|
97
|
+
pathTolerance: 0.9,
|
|
98
|
+
sortAttrs: true,
|
|
99
|
+
removeViewBox: false,
|
|
100
|
+
enableNumericOptimization: true,
|
|
101
|
+
enableStyleOptimization: true,
|
|
102
|
+
enableTransformOptimization: true,
|
|
103
|
+
enableTransformCollapsing: true,
|
|
104
|
+
enablePathOptimization: true,
|
|
105
|
+
enablePathSimplification: true,
|
|
106
|
+
shapeConversion: true,
|
|
107
|
+
shapeConversionThreshold: 0,
|
|
108
|
+
},
|
|
109
|
+
};
|
|
110
|
+
const overrides = levelOverrides[level] ?? {};
|
|
111
|
+
return { ...baseConfig, ...overrides };
|
|
122
112
|
}
|
|
@@ -187,7 +187,7 @@ export class SVGProcessor {
|
|
|
187
187
|
const baseName = path.basename(fileName, '.svg');
|
|
188
188
|
// Object lookup map for naming conventions - O(1) performance
|
|
189
189
|
const namingHandlers = {
|
|
190
|
-
kebab: () =>
|
|
190
|
+
kebab: () => toKebabCase(baseName),
|
|
191
191
|
camel: () => {
|
|
192
192
|
const pascalName = toPascalCase(baseName);
|
|
193
193
|
return pascalName.charAt(0).toLowerCase() + pascalName.slice(1);
|
|
@@ -340,13 +340,26 @@ export class SVGProcessor {
|
|
|
340
340
|
error: error,
|
|
341
341
|
};
|
|
342
342
|
logger.error(`Failed to process ${svgFilePath}:`, error);
|
|
343
|
+
// Immediately remove failed jobs to prevent memory leaks
|
|
344
|
+
this.processingQueue.delete(jobId);
|
|
343
345
|
return result;
|
|
344
346
|
}
|
|
345
347
|
finally {
|
|
346
|
-
// Clean up completed jobs after
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
348
|
+
// Clean up completed jobs after a short delay (allows stats queries)
|
|
349
|
+
if (job.status === 'completed') {
|
|
350
|
+
setTimeout(() => {
|
|
351
|
+
this.processingQueue.delete(jobId);
|
|
352
|
+
}, 30000); // 30 seconds
|
|
353
|
+
}
|
|
354
|
+
// Cap queue size as a safety net to prevent unbounded growth
|
|
355
|
+
if (this.processingQueue.size > 10000) {
|
|
356
|
+
const oldestEntries = Array.from(this.processingQueue.entries())
|
|
357
|
+
.filter(([, j]) => j.status === 'completed' || j.status === 'failed')
|
|
358
|
+
.slice(0, this.processingQueue.size - 5000);
|
|
359
|
+
for (const [key] of oldestEntries) {
|
|
360
|
+
this.processingQueue.delete(key);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
350
363
|
}
|
|
351
364
|
}
|
|
352
365
|
/**
|
|
@@ -354,7 +367,7 @@ export class SVGProcessor {
|
|
|
354
367
|
*/
|
|
355
368
|
getProcessingStats() {
|
|
356
369
|
const jobs = Array.from(this.processingQueue.values());
|
|
357
|
-
//
|
|
370
|
+
// Direct property increment via object key — O(1) per job, avoids switch branching
|
|
358
371
|
const stats = {
|
|
359
372
|
total: jobs.length,
|
|
360
373
|
pending: 0,
|
|
@@ -363,20 +376,7 @@ export class SVGProcessor {
|
|
|
363
376
|
failed: 0,
|
|
364
377
|
};
|
|
365
378
|
for (const job of jobs) {
|
|
366
|
-
|
|
367
|
-
case 'pending':
|
|
368
|
-
stats.pending++;
|
|
369
|
-
break;
|
|
370
|
-
case 'processing':
|
|
371
|
-
stats.processing++;
|
|
372
|
-
break;
|
|
373
|
-
case 'completed':
|
|
374
|
-
stats.completed++;
|
|
375
|
-
break;
|
|
376
|
-
case 'failed':
|
|
377
|
-
stats.failed++;
|
|
378
|
-
break;
|
|
379
|
-
}
|
|
379
|
+
stats[job.status]++;
|
|
380
380
|
}
|
|
381
381
|
return stats;
|
|
382
382
|
}
|
package/dist/services/config.js
CHANGED
|
@@ -275,10 +275,14 @@ export class ConfigService {
|
|
|
275
275
|
logger.info(`Migrated optimization level: ${oldOptimization} → ${migratedConfig.performance.optimization}`);
|
|
276
276
|
}
|
|
277
277
|
}
|
|
278
|
-
// Save migrated config
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
278
|
+
// Save migrated config merged with defaults to ensure all new keys exist
|
|
279
|
+
const mergedMigratedConfig = {
|
|
280
|
+
...this.getDefaultConfig(),
|
|
281
|
+
...migratedConfig,
|
|
282
|
+
};
|
|
283
|
+
this.writeConfig(mergedMigratedConfig);
|
|
284
|
+
logger.success(`Configuration migrated from ${currentVersion} to ${CURRENT_VERSION}`);
|
|
285
|
+
return mergedMigratedConfig;
|
|
282
286
|
}
|
|
283
287
|
/**
|
|
284
288
|
* Validate configuration
|
|
@@ -25,8 +25,15 @@ export class SVGService {
|
|
|
25
25
|
* Set optimizer level for SVG processing
|
|
26
26
|
*/
|
|
27
27
|
setOptimizerLevel(level) {
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
// O(1) Set lookup instead of O(n) Array.includes()
|
|
29
|
+
const validLevels = new Set([
|
|
30
|
+
'none',
|
|
31
|
+
'basic',
|
|
32
|
+
'balanced',
|
|
33
|
+
'aggressive',
|
|
34
|
+
'maximum',
|
|
35
|
+
]);
|
|
36
|
+
if (!validLevels.has(level.toLowerCase())) {
|
|
30
37
|
logger.warn(`Invalid optimization level "${level}". Using "basic" instead.`);
|
|
31
38
|
svgProcessor.setOptimizationLevel(OptLevel.BASIC);
|
|
32
39
|
return;
|
package/dist/utils/native.d.ts
CHANGED
|
@@ -45,7 +45,6 @@ export declare class FileSystem {
|
|
|
45
45
|
private static _readdir;
|
|
46
46
|
private static _stat;
|
|
47
47
|
private static _mkdir;
|
|
48
|
-
private static _rmdir;
|
|
49
48
|
private static _unlink;
|
|
50
49
|
static exists(path: string): Promise<boolean>;
|
|
51
50
|
static readFile(path: string, encoding?: BufferEncoding): Promise<string>;
|
package/dist/utils/native.js
CHANGED
|
@@ -62,7 +62,6 @@ export class FileSystem {
|
|
|
62
62
|
static _readdir = promisify(fs.readdir);
|
|
63
63
|
static _stat = promisify(fs.stat);
|
|
64
64
|
static _mkdir = promisify(fs.mkdir);
|
|
65
|
-
static _rmdir = promisify(fs.rmdir);
|
|
66
65
|
static _unlink = promisify(fs.unlink);
|
|
67
66
|
static async exists(path) {
|
|
68
67
|
try {
|
|
@@ -94,18 +93,7 @@ export class FileSystem {
|
|
|
94
93
|
}
|
|
95
94
|
static async removeDir(dirPath) {
|
|
96
95
|
try {
|
|
97
|
-
|
|
98
|
-
for (const file of files) {
|
|
99
|
-
const filePath = `${dirPath}/${file}`;
|
|
100
|
-
const stats = await this._stat(filePath);
|
|
101
|
-
if (stats.isDirectory()) {
|
|
102
|
-
await this.removeDir(filePath);
|
|
103
|
-
}
|
|
104
|
-
else {
|
|
105
|
-
await this._unlink(filePath);
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
await this._rmdir(dirPath);
|
|
96
|
+
await fs.promises.rm(dirPath, { recursive: true, force: true });
|
|
109
97
|
}
|
|
110
98
|
catch (error) {
|
|
111
99
|
if (error.code !== 'ENOENT') {
|
|
@@ -311,10 +299,14 @@ export class FileWatcher {
|
|
|
311
299
|
this.emit(eventType, `${path}/${filename}`);
|
|
312
300
|
}
|
|
313
301
|
});
|
|
302
|
+
watcher.on('error', error => {
|
|
303
|
+
this.emit('error', error);
|
|
304
|
+
});
|
|
314
305
|
this.watchers.push(watcher);
|
|
315
306
|
}
|
|
316
307
|
catch (error) {
|
|
317
|
-
|
|
308
|
+
// Emit error event so consumers can handle it
|
|
309
|
+
this.emit('error', error);
|
|
318
310
|
}
|
|
319
311
|
return this;
|
|
320
312
|
}
|
package/dist/watch.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import path from 'path';
|
|
2
2
|
import { generateSVG } from './builder.js';
|
|
3
3
|
import { isLocked } from './lock.js';
|
|
4
|
-
import { FileSystem, FileWatcher } from './utils/native.js';
|
|
4
|
+
import { FileSystem, FileWatcher, toPascalCase } from './utils/native.js';
|
|
5
5
|
/**
|
|
6
6
|
* Watches a source folder for changes to SVG files and automatically
|
|
7
7
|
* rebuilds React components when SVGs are added, modified, or deleted.
|
|
@@ -59,8 +59,9 @@ export async function watchSVGs(config) {
|
|
|
59
59
|
await generateSVG({ svgFile: filePath, outDir });
|
|
60
60
|
}
|
|
61
61
|
else {
|
|
62
|
-
// File was deleted
|
|
63
|
-
const
|
|
62
|
+
// File was deleted — use PascalCase to match the generated component filename
|
|
63
|
+
const baseName = path.basename(filePath, '.svg');
|
|
64
|
+
const componentName = toPascalCase(baseName);
|
|
64
65
|
const outFile = path.join(outDir, `${componentName}.tsx`);
|
|
65
66
|
if (await FileSystem.exists(outFile)) {
|
|
66
67
|
await FileSystem.unlink(outFile);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Error Handling Standards
|
|
2
2
|
|
|
3
3
|
## Purpose
|
|
4
|
-
This document establishes standardized error handling patterns for SVGER-CLI v4.0.
|
|
4
|
+
This document establishes standardized error handling patterns for SVGER-CLI v4.0.3+ to ensure consistency, proper error propagation, and appropriate user feedback.
|
|
5
5
|
|
|
6
6
|
## Standard Patterns
|
|
7
7
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "svger-cli",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.3",
|
|
4
4
|
"description": "Enterprise-grade SVG to component converter with advanced plugin system, visual diff testing, and official framework integrations. Supporting React, React Native, Vue, Angular, Svelte, Solid, Lit, Preact & Vanilla. Features TypeScript, HMR, optimization pipeline, and extensible architecture.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|