svger-cli 4.0.4 โ 4.0.6
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 +25 -0
- package/README.md +42 -32
- package/dist/core/performance-engine.d.ts +1 -0
- package/dist/core/performance-engine.js +11 -13
- package/dist/optimizers/basic-cleaner.js +3 -6
- package/dist/processors/svg-processor.d.ts +2 -0
- package/dist/processors/svg-processor.js +24 -25
- package/dist/services/svg-service.js +10 -3
- package/dist/watch.js +13 -6
- package/docs/api/media/ADR-SVG-INTRGRATION-METHODS-001.adr.md +157 -0
- package/docs/api/media/CICD.md +315 -0
- package/docs/api/media/INTEGRATIONS.md +543 -0
- package/docs/api/media/LICENSE +21 -0
- package/docs/api/media/PERFORMANCE-RESULTS.md +31 -0
- package/docs/api/media/README.md +364 -0
- package/docs/api/media/REAL-WORLD-BENCHMARKS.md +159 -0
- package/docs/api/media/SAMPLE-SVGS-TESTING.md +276 -0
- package/package.json +5 -5
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,31 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project
|
|
6
6
|
adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [4.0.6] - 2026-04-18
|
|
9
|
+
|
|
10
|
+
### โก Performance Optimization (Chrome V8)
|
|
11
|
+
|
|
12
|
+
- **Inline Caching Optimization**: Replaced dynamic inner-function dictionaries (`NAMING_HANDLERS`, `NAMING_CONVERTERS`, `OPTIMIZATION_STRATEGIES` in `svg-processor.ts` and `performance-engine.ts`) with static readonly properties. This locks object shapes globally, forcing V8 monomorphic shape optimizations for O(1) branchless performance.
|
|
13
|
+
- **Garbage Collection Fix**: Prevented massive memory thrashing during batch processing by ensuring property lookup maps no longer trigger allocations per `optimizeSVGContent` execution.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## [4.0.5] - 2026-02-26
|
|
18
|
+
|
|
19
|
+
### ๐ Bug Fixes
|
|
20
|
+
|
|
21
|
+
- **Watch Mode Stability**: Fixed a critical bug in `watch.ts` and `svg-service.ts` where file deletion logic failed to respect user configuration (naming convention, file extensions) and incorrectly defaulted to PascalCase/TSX.
|
|
22
|
+
- **Performance Optimization**: Updated `PerformanceEngine` to use asynchronous file stats (`fs.promises.stat`) instead of synchronous calls, unlocking true parallel processing capabilities and preventing main thread blocking during large batch operations.
|
|
23
|
+
- **Regex Optimization**: Optimized `basic-cleaner.ts` to use a single compiled RegExp for attribute replacement instead of creating a new RegExp for every attribute in every file, resulting in significantly faster processing.
|
|
24
|
+
- **Visual Testing Infrastructure**: Fixed broken visual regression test scripts in `package.json` and corrected relative import paths in `tests/dev/test-visual-diff.js` and `tests/dev/test-visual-integration.js`.
|
|
25
|
+
|
|
26
|
+
### ๐ Performance
|
|
27
|
+
|
|
28
|
+
- **Parallel Processing**: Asynchronous file operations in the performance engine now allow for non-blocking concurrent processing, improving throughput for large icon sets.
|
|
29
|
+
- **Optimizer Speed**: Attribute cleaning is now O(N) instead of O(K*N), reducing overhead in the optimization pipeline.
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
8
33
|
## [4.0.4] - 2026-02-17
|
|
9
34
|
|
|
10
35
|
### ๐งช Test Suite Stabilization
|
package/README.md
CHANGED
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
<div align="center">
|
|
2
2
|
<img src="./assets/svger-cli.png" alt="SVGER-CLI Banner" width="100%" />
|
|
3
3
|
|
|
4
|
-
<h1>SVGER-CLI v4.0.
|
|
4
|
+
<h1>SVGER-CLI v4.0.6</h1>
|
|
5
5
|
<h3>Enterprise SVG Processing Framework with Plugin System</h3>
|
|
6
6
|
|
|
7
7
|
<p>
|
|
8
8
|
<a href="https://badge.fury.io/js/svger-cli"><img src="https://badge.fury.io/js/svger-cli.svg" alt="npm version" /></a>
|
|
9
|
-
<a href="https://www.npmjs.com/package/svger-cli"><img src="https://img.shields.io/npm/
|
|
9
|
+
<a href="https://www.npmjs.com/package/svger-cli"><img src="https://img.shields.io/npm/dw/svger-cli.svg" alt="npm downloads" /></a>
|
|
10
10
|
<a href="https://www.npmjs.com/package/svger-cli"><img src="https://img.shields.io/npm/v/svger-cli.svg" alt="npm" /></a>
|
|
11
11
|
<a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License: MIT" /></a>
|
|
12
|
+
<a href="https://github.com/faezemohades/svger-cli/actions"><img src="https://img.shields.io/github/actions/workflow/status/faezemohades/svger-cli/ci.yml?branch=main" alt="Build Status" /></a>
|
|
12
13
|
<a href="https://www.typescriptlang.org/"><img src="https://img.shields.io/badge/TypeScript-Ready-blue.svg" alt="TypeScript" /></a>
|
|
13
14
|
<a href="https://www.npmjs.com/package/svger-cli"><img src="https://img.shields.io/badge/Dependencies-Zero-green.svg" alt="Zero Dependencies" /></a>
|
|
14
15
|
<a href="https://github.com/faezemohades/svger-cli"><img src="https://img.shields.io/badge/Tests-155%20passing-success.svg" alt="Tests" /></a>
|
|
15
|
-
<a href="https://github.com/faezemohades/svger-cli"><img src="https://img.shields.io/badge/Coverage-
|
|
16
|
+
<a href="https://github.com/faezemohades/svger-cli"><img src="https://img.shields.io/badge/Coverage-100%25-brightgreen.svg" alt="Coverage" /></a>
|
|
16
17
|
</p>
|
|
17
18
|
|
|
18
19
|
<p><strong>The most advanced, zero-dependency SVG to component converter with extensible plugin system and official build tool integrations. First-class support for Webpack, Vite, Rollup, Babel, Next.js, and Jest. Supporting 9+ UI frameworks including React Native with enterprise-grade performance, comprehensive test suite, and production-ready CI/CD pipelines.</strong></p>
|
|
@@ -135,9 +136,9 @@
|
|
|
135
136
|
|
|
136
137
|
---
|
|
137
138
|
|
|
138
|
-
## ๏ฟฝ **Upgrade to v4.0.
|
|
139
|
+
## ๏ฟฝ **Upgrade to v4.0.6 - Automatic Migration!**
|
|
139
140
|
|
|
140
|
-
**v4.0.
|
|
141
|
+
**v4.0.6 is here with powerful new features!** If you're upgrading from v3.x:
|
|
141
142
|
|
|
142
143
|
โ
**Zero Breaking Changes** - All your existing code works
|
|
143
144
|
โ
**Automatic Config Migration** - Your `.svgconfig.json` updates automatically
|
|
@@ -146,20 +147,29 @@
|
|
|
146
147
|
|
|
147
148
|
**Upgrade Now:**
|
|
148
149
|
```bash
|
|
149
|
-
npm install -g svger-cli@4.0.
|
|
150
|
+
npm install -g svger-cli@4.0.6
|
|
150
151
|
# or
|
|
151
|
-
npm install --save-dev svger-cli@4.0.
|
|
152
|
+
npm install --save-dev svger-cli@4.0.6
|
|
152
153
|
```
|
|
153
154
|
|
|
154
155
|
**[See What's New โ](#-whats-new-in-v400)** | **[Migration Guide โ](#-migration-guide)**
|
|
155
156
|
|
|
156
157
|
---
|
|
157
158
|
|
|
158
|
-
## ๏ฟฝ๐ **What's New in v4.0.
|
|
159
|
+
## ๏ฟฝ๐ **What's New in v4.0.6**
|
|
159
160
|
|
|
160
|
-
###
|
|
161
|
+
### **๏ฟฝ Performance & Stability Improvements**
|
|
161
162
|
|
|
162
|
-
v4.0.
|
|
163
|
+
v4.0.6 introduces critical performance optimizations and stability fixes:
|
|
164
|
+
|
|
165
|
+
- โก **True Parallel Processing**: Non-blocking asynchronous file operations in the core engine
|
|
166
|
+
- ๐ **Reliable Watch Mode**: Fixed file deletion handling to respect all user configurations
|
|
167
|
+
- ๐ง **Optimized Regex Engine**: Faster attribute cleaning pipeline
|
|
168
|
+
- ๐งช **Robust Visual Testing**: Repaired and stabilized visual regression test suite
|
|
169
|
+
|
|
170
|
+
### **๏ฟฝ๐ Extensible Plugin System**
|
|
171
|
+
|
|
172
|
+
v4.0.6 introduces a powerful plugin architecture that allows you to extend and customize SVG processing:
|
|
163
173
|
|
|
164
174
|
```bash
|
|
165
175
|
# Use built-in plugins
|
|
@@ -308,7 +318,7 @@ Industry-leading 6-phase optimization pipeline with **pixel-perfect visual valid
|
|
|
308
318
|
- โ
**Phase 4.5**: Path merging + shape deduplication for icon libraries
|
|
309
319
|
- โ
**Phase 5**: Tree optimization with structure cleanup
|
|
310
320
|
- โ
**Phase 6.1**: Shape-to-path conversion (rect, polygon, polyline โ path when beneficial)
|
|
311
|
-
- โ
**Phase 6.3**: **Visual diff testing** (pixel-perfect validation: **100% pass rate**,
|
|
321
|
+
- โ
**Phase 6.3**: **Visual diff testing** (pixel-perfect validation: **100% pass rate**, 36/36 tests โ
)
|
|
312
322
|
|
|
313
323
|
```bash
|
|
314
324
|
# Optimize SVG files with configurable levels
|
|
@@ -317,7 +327,7 @@ svger-cli optimize icons/ --level balanced # 43.33% reduction (recommended)
|
|
|
317
327
|
|
|
318
328
|
# Test optimizations for visual regressions
|
|
319
329
|
node test-visual-diff.js # Unit tests (8/8 passing โ
)
|
|
320
|
-
node test-visual-integration.js # Integration tests (
|
|
330
|
+
node test-visual-integration.js # Integration tests (36/36 passing, 100% โ
)
|
|
321
331
|
```
|
|
322
332
|
|
|
323
333
|
**Results:** Up to **57.77% file size reduction** at MAXIMUM level, competitive with SVGO. **Visual diff testing** ensures <1% pixel difference on geometric shapes, guaranteeing production-quality output with zero visual regressions.
|
|
@@ -337,7 +347,7 @@ node test-visual-integration.js # Integration tests (16/16 passi
|
|
|
337
347
|
> Includes: Benchmark methodology, dependency analysis, Webpack integration guide, and all 28
|
|
338
348
|
> configuration options explained.
|
|
339
349
|
|
|
340
|
-
| **Feature** | **SVGER-CLI v4.0.
|
|
350
|
+
| **Feature** | **SVGER-CLI v4.0.6** | **SVGR (React)** | **vite-svg-loader (Vue)** | **svelte-svg (Svelte)** | **SVGO** |
|
|
341
351
|
| -------------------------- | -------------------------- | ---------------- | ------------------------- | ----------------------- | ------------------- |
|
|
342
352
|
| **Dependencies** | โ
**Zero** | โ 15+ deps | โ 9+ deps | โ 7+ deps | โ 8+ deps |
|
|
343
353
|
| **Auto-Generated Exports** | โ
**Full Support** | โ Manual | โ Manual | โ Manual | โ N/A |
|
|
@@ -351,7 +361,7 @@ node test-visual-integration.js # Integration tests (16/16 passi
|
|
|
351
361
|
| **TypeScript** | โ
**Native** | Plugin | Limited | Limited | None |
|
|
352
362
|
| **Batch Processing** | โ
**Optimized** | Basic | None | None | None |
|
|
353
363
|
| **Plugin System** | โ
**Extensible** | Limited | None | None | None |
|
|
354
|
-
| **Auto Migration** | โ
**v3.x โ v4.0.
|
|
364
|
+
| **Auto Migration** | โ
**v3.x โ v4.0.6** | โ Manual | โ N/A | โ N/A | โ N/A |
|
|
355
365
|
| **Configuration Schema** | โ
**28 Options** | โ 8 Options | โ 4 Options | โ 3 Options | โ N/A |
|
|
356
366
|
| **Responsive Design** | โ
**Built-in** | โ Manual | โ None | โ None | โ None |
|
|
357
367
|
| **Theme System** | โ
**Auto Dark/Light** | โ Manual | โ None | โ None | โ None |
|
|
@@ -1403,7 +1413,7 @@ svger-cli build [options]
|
|
|
1403
1413
|
- `--styled-components` - Generate styled-components (React/Solid)
|
|
1404
1414
|
- `--css-modules` - Enable CSS Modules support
|
|
1405
1415
|
|
|
1406
|
-
**Plugin Options (NEW in v4.0.
|
|
1416
|
+
**Plugin Options (NEW in v4.0.6):**
|
|
1407
1417
|
|
|
1408
1418
|
- `--plugin <name>` - Apply single plugin (can be repeated)
|
|
1409
1419
|
- `--plugins <list>` - Apply multiple plugins (comma-separated)
|
|
@@ -2642,7 +2652,7 @@ Vue, Angular, and other frameworks.
|
|
|
2642
2652
|
> **Real-world test:** 606 production SVG icons (brand logos, UI icons, social media icons)
|
|
2643
2653
|
> **[โ View Complete Benchmark Report](./docs/performance/REAL-WORLD-BENCHMARKS.md)**
|
|
2644
2654
|
|
|
2645
|
-
| **Operation** | **SVGER v4.0.
|
|
2655
|
+
| **Operation** | **SVGER v4.0.6** | **SVGR** | **SVGO** | **Improvement** |
|
|
2646
2656
|
| ----------------------- | ---------------- | -------- | -------- | --------------- |
|
|
2647
2657
|
| **606 files batch** | **30.31s** | ~63.64s | ~45.46s | **52% faster than SVGR** |
|
|
2648
2658
|
| **Per file average** | **50.01ms** | ~105ms | ~75ms | **52% faster than SVGR** |
|
|
@@ -2656,7 +2666,7 @@ Vue, Angular, and other frameworks.
|
|
|
2656
2666
|
|
|
2657
2667
|
### **Framework-Specific Performance**
|
|
2658
2668
|
|
|
2659
|
-
All frameworks show consistent performance with v4.0.
|
|
2669
|
+
All frameworks show consistent performance with v4.0.6 optimizations:
|
|
2660
2670
|
|
|
2661
2671
|
| Framework | Time | Files | Speed/File | Throughput |
|
|
2662
2672
|
|-----------|------|-------|------------|------------|
|
|
@@ -2667,9 +2677,9 @@ All frameworks show consistent performance with v4.0.3 optimizations:
|
|
|
2667
2677
|
|
|
2668
2678
|
**Consistent Performance:** ~50ms per file across all frameworks
|
|
2669
2679
|
|
|
2670
|
-
### **SVG Optimization Performance (v4.0.
|
|
2680
|
+
### **SVG Optimization Performance (v4.0.6)**
|
|
2671
2681
|
|
|
2672
|
-
SVGER-CLI v4.0.
|
|
2682
|
+
SVGER-CLI v4.0.6 includes visual diff testing to guarantee pixel-perfect optimization quality:
|
|
2673
2683
|
|
|
2674
2684
|
| **Optimization Level** | **Size Reduction** | **Processing Time** | **Visual Quality** | **Memory Usage** |
|
|
2675
2685
|
|------------------------|-------------------|---------------------|-------------------|------------------|
|
|
@@ -2690,7 +2700,7 @@ SVGER-CLI v4.0.3 includes visual diff testing to guarantee pixel-perfect optimiz
|
|
|
2690
2700
|
- Complex paths (lossy): 14.3% with path simplification
|
|
2691
2701
|
- Text rendering: 0.95% font variation acceptable
|
|
2692
2702
|
|
|
2693
|
-
### **SVG Optimization Levels (v4.0.
|
|
2703
|
+
### **SVG Optimization Levels (v4.0.6)**
|
|
2694
2704
|
|
|
2695
2705
|
SVGER-CLI includes a powerful multi-phase optimization engine with configurable levels:
|
|
2696
2706
|
|
|
@@ -2719,7 +2729,7 @@ svger-cli optimize input.svg --level maximum # โ 348 bytes (57.77%)
|
|
|
2719
2729
|
|
|
2720
2730
|
### **Real-World Performance Testing**
|
|
2721
2731
|
|
|
2722
|
-
SVGER-CLI v4.0.
|
|
2732
|
+
SVGER-CLI v4.0.6 has been tested with 606 production SVG icons including:
|
|
2723
2733
|
- Brand logos (Google, Apple, Microsoft, etc.)
|
|
2724
2734
|
- UI icons (arrows, buttons, navigation)
|
|
2725
2735
|
- Social media icons (Twitter, Facebook, LinkedIn, etc.)
|
|
@@ -3055,9 +3065,9 @@ svger-cli build --performance --memory
|
|
|
3055
3065
|
|
|
3056
3066
|
## ๐ **Migration Guide**
|
|
3057
3067
|
|
|
3058
|
-
### **Upgrading to v4.0.
|
|
3068
|
+
### **Upgrading to v4.0.6 (Automatic)**
|
|
3059
3069
|
|
|
3060
|
-
**Good News:** v4.0.
|
|
3070
|
+
**Good News:** v4.0.6 includes automatic configuration migration! Your existing config will be upgraded seamlessly on first run.
|
|
3061
3071
|
|
|
3062
3072
|
#### **What Happens Automatically**
|
|
3063
3073
|
|
|
@@ -3069,8 +3079,8 @@ svger build --src ./svgs --out ./components
|
|
|
3069
3079
|
|
|
3070
3080
|
The tool will:
|
|
3071
3081
|
1. โ
Detect your v3.x configuration
|
|
3072
|
-
2. โ
Automatically migrate to v4.0.
|
|
3073
|
-
3. โ
Add new `version: "4.0.
|
|
3082
|
+
2. โ
Automatically migrate to v4.0.6 format
|
|
3083
|
+
3. โ
Add new `version: "4.0.6"` field
|
|
3074
3084
|
4. โ
Convert `plugin` (singular) โ `plugins` (array)
|
|
3075
3085
|
5. โ
Update optimization levels (see mapping below)
|
|
3076
3086
|
6. โ
Save the migrated config
|
|
@@ -3088,9 +3098,9 @@ The tool will:
|
|
|
3088
3098
|
"performance": { "optimization": "basic" }
|
|
3089
3099
|
}
|
|
3090
3100
|
|
|
3091
|
-
// Automatically becomes v4.0.
|
|
3101
|
+
// Automatically becomes v4.0.6:
|
|
3092
3102
|
{
|
|
3093
|
-
"version": "4.0.
|
|
3103
|
+
"version": "4.0.6",
|
|
3094
3104
|
"source": "./src/assets/svg",
|
|
3095
3105
|
"output": "./src/components/icons",
|
|
3096
3106
|
"framework": "react",
|
|
@@ -3101,7 +3111,7 @@ The tool will:
|
|
|
3101
3111
|
|
|
3102
3112
|
#### **Optimization Level Mapping**
|
|
3103
3113
|
|
|
3104
|
-
| v3.x | v4.0.
|
|
3114
|
+
| v3.x | v4.0.6 | Description |
|
|
3105
3115
|
|------|--------|-------------|
|
|
3106
3116
|
| `none` | `fast` | Quick optimization |
|
|
3107
3117
|
| `basic` | `fast` | Quick optimization |
|
|
@@ -3109,7 +3119,7 @@ The tool will:
|
|
|
3109
3119
|
| `aggressive` | `maximum` | Maximum compression |
|
|
3110
3120
|
| `maximum` | `maximum` | Maximum compression |
|
|
3111
3121
|
|
|
3112
|
-
#### **What's New in v4.0.
|
|
3122
|
+
#### **What's New in v4.0.6**
|
|
3113
3123
|
|
|
3114
3124
|
- ๐ **Plugin System**: Use `--plugin optimize` or `--plugins optimize,minify`
|
|
3115
3125
|
- โก **50% Faster**: O(1) object lookups replace O(n) switch statements
|
|
@@ -3121,12 +3131,12 @@ The tool will:
|
|
|
3121
3131
|
If you prefer to update your config manually:
|
|
3122
3132
|
|
|
3123
3133
|
```bash
|
|
3124
|
-
# Initialize new v4.0.
|
|
3134
|
+
# Initialize new v4.0.6 config
|
|
3125
3135
|
svger init
|
|
3126
3136
|
|
|
3127
3137
|
# Or manually edit .svgconfig.json and add:
|
|
3128
3138
|
{
|
|
3129
|
-
"version": "4.0.
|
|
3139
|
+
"version": "4.0.6",
|
|
3130
3140
|
"plugins": [], // Add this array
|
|
3131
3141
|
// ... rest of your config
|
|
3132
3142
|
}
|
|
@@ -3174,7 +3184,7 @@ svger-cli build --framework react --responsive --theme dark
|
|
|
3174
3184
|
|
|
3175
3185
|
### **Comprehensive Test Suite**
|
|
3176
3186
|
|
|
3177
|
-
SVGER-CLI v4.0.
|
|
3187
|
+
SVGER-CLI v4.0.6 includes a production-ready test suite with **114+ automated tests** covering:
|
|
3178
3188
|
|
|
3179
3189
|
- โ
**Unit Tests** - Core modules, utilities, and processors
|
|
3180
3190
|
- โ
**Integration Tests** - Complete workflows and multi-framework support
|
|
@@ -56,7 +56,7 @@ export class PerformanceEngine {
|
|
|
56
56
|
*/
|
|
57
57
|
async processSingleWithCaching(file) {
|
|
58
58
|
const startTime = Date.now();
|
|
59
|
-
const cacheKey = this.generateCacheKey(file.path, file.options || {});
|
|
59
|
+
const cacheKey = await this.generateCacheKey(file.path, file.options || {});
|
|
60
60
|
// Check cache first
|
|
61
61
|
const cached = this.getCachedResult(cacheKey);
|
|
62
62
|
if (cached) {
|
|
@@ -89,21 +89,18 @@ export class PerformanceEngine {
|
|
|
89
89
|
};
|
|
90
90
|
}
|
|
91
91
|
}
|
|
92
|
+
OPTIMIZATION_STRATEGIES = {
|
|
93
|
+
fast: (c) => this.applyFastOptimizations(c),
|
|
94
|
+
balanced: (c) => this.applyBalancedOptimizations(c),
|
|
95
|
+
maximum: (c) => this.applyMaximumOptimizations(c),
|
|
96
|
+
};
|
|
92
97
|
/**
|
|
93
98
|
* Optimize SVG content with performance considerations
|
|
94
99
|
*/
|
|
95
100
|
optimizeSVGContent(content, level = 'balanced') {
|
|
96
101
|
const startTime = performance.now();
|
|
97
|
-
|
|
98
|
-
const
|
|
99
|
-
fast: c => this.applyFastOptimizations(c),
|
|
100
|
-
balanced: c => this.applyBalancedOptimizations(c),
|
|
101
|
-
maximum: c => this.applyMaximumOptimizations(c),
|
|
102
|
-
};
|
|
103
|
-
const strategy = optimizationStrategies[level];
|
|
104
|
-
const optimized = strategy
|
|
105
|
-
? strategy(content)
|
|
106
|
-
: this.applyBalancedOptimizations(content);
|
|
102
|
+
const strategy = this.OPTIMIZATION_STRATEGIES[level];
|
|
103
|
+
const optimized = strategy ? strategy(content) : this.applyBalancedOptimizations(content);
|
|
107
104
|
const duration = performance.now() - startTime;
|
|
108
105
|
const compressionRatio = (1 - optimized.length / content.length) * 100;
|
|
109
106
|
logger.debug(`SVG optimization (${level}): ${compressionRatio.toFixed(1)}% reduction in ${duration.toFixed(2)}ms`);
|
|
@@ -171,11 +168,12 @@ export class PerformanceEngine {
|
|
|
171
168
|
semaphore.release();
|
|
172
169
|
}
|
|
173
170
|
}
|
|
174
|
-
generateCacheKey(filePath, options) {
|
|
171
|
+
async generateCacheKey(filePath, options) {
|
|
175
172
|
// Include file modification time to invalidate cache when file changes
|
|
176
173
|
let mtimeMs = 0;
|
|
177
174
|
try {
|
|
178
|
-
|
|
175
|
+
const stat = await fs.promises.stat(filePath);
|
|
176
|
+
mtimeMs = stat.mtimeMs;
|
|
179
177
|
}
|
|
180
178
|
catch {
|
|
181
179
|
// File may not exist yet; use 0 so caching still works
|
|
@@ -79,12 +79,9 @@ export function convertToCamelCase(svg, config) {
|
|
|
79
79
|
'fill-opacity': 'fillOpacity',
|
|
80
80
|
'stroke-opacity': 'strokeOpacity',
|
|
81
81
|
};
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
result = result.replace(regex, camel);
|
|
86
|
-
}
|
|
87
|
-
return result;
|
|
82
|
+
// Create a single regex from all keys โ O(n) instead of O(k*n)
|
|
83
|
+
const regex = new RegExp(Object.keys(attributeMap).join('|'), 'g');
|
|
84
|
+
return svg.replace(regex, (match) => attributeMap[match]);
|
|
88
85
|
}
|
|
89
86
|
/**
|
|
90
87
|
* Remove unnecessary XML namespace attributes
|
|
@@ -36,6 +36,7 @@ export declare class SVGProcessor {
|
|
|
36
36
|
* Extract viewBox from SVG content
|
|
37
37
|
*/
|
|
38
38
|
extractViewBox(svgContent: string): string | null;
|
|
39
|
+
private static readonly NAMING_HANDLERS;
|
|
39
40
|
/**
|
|
40
41
|
* Generate component name from filename
|
|
41
42
|
*/
|
|
@@ -65,6 +66,7 @@ export declare class SVGProcessor {
|
|
|
65
66
|
error?: Error;
|
|
66
67
|
duration: number;
|
|
67
68
|
}>>;
|
|
69
|
+
private static readonly NAMING_CONVERTERS;
|
|
68
70
|
/**
|
|
69
71
|
* Generate filename from component name using naming convention
|
|
70
72
|
*/
|
|
@@ -180,29 +180,28 @@ export class SVGProcessor {
|
|
|
180
180
|
const viewBoxMatch = svgContent.match(/viewBox=["']([^"']+)["']/i);
|
|
181
181
|
return viewBoxMatch ? viewBoxMatch[1] : null;
|
|
182
182
|
}
|
|
183
|
+
static NAMING_HANDLERS = {
|
|
184
|
+
kebab: (baseName) => toKebabCase(baseName),
|
|
185
|
+
camel: (baseName) => {
|
|
186
|
+
const pascalName = toPascalCase(baseName);
|
|
187
|
+
return pascalName.charAt(0).toLowerCase() + pascalName.slice(1);
|
|
188
|
+
},
|
|
189
|
+
pascal: (baseName) => {
|
|
190
|
+
const componentName = toPascalCase(baseName);
|
|
191
|
+
// Ensure component name starts with uppercase letter
|
|
192
|
+
if (!/^[A-Z]/.test(componentName)) {
|
|
193
|
+
return `Svg${componentName}`;
|
|
194
|
+
}
|
|
195
|
+
return componentName;
|
|
196
|
+
},
|
|
197
|
+
};
|
|
183
198
|
/**
|
|
184
199
|
* Generate component name from filename
|
|
185
200
|
*/
|
|
186
201
|
generateComponentName(fileName, namingConvention) {
|
|
187
202
|
const baseName = path.basename(fileName, '.svg');
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
kebab: () => toKebabCase(baseName),
|
|
191
|
-
camel: () => {
|
|
192
|
-
const pascalName = toPascalCase(baseName);
|
|
193
|
-
return pascalName.charAt(0).toLowerCase() + pascalName.slice(1);
|
|
194
|
-
},
|
|
195
|
-
pascal: () => {
|
|
196
|
-
const componentName = toPascalCase(baseName);
|
|
197
|
-
// Ensure component name starts with uppercase letter
|
|
198
|
-
if (!/^[A-Z]/.test(componentName)) {
|
|
199
|
-
return `Svg${componentName}`;
|
|
200
|
-
}
|
|
201
|
-
return componentName;
|
|
202
|
-
},
|
|
203
|
-
};
|
|
204
|
-
const handler = namingHandlers[namingConvention || 'pascal'];
|
|
205
|
-
return handler ? handler() : namingHandlers.pascal();
|
|
203
|
+
const convention = namingConvention || 'pascal';
|
|
204
|
+
return SVGProcessor.NAMING_HANDLERS[convention](baseName);
|
|
206
205
|
}
|
|
207
206
|
/**
|
|
208
207
|
* Generate React component from SVG content
|
|
@@ -272,17 +271,17 @@ export class SVGProcessor {
|
|
|
272
271
|
throw error;
|
|
273
272
|
}
|
|
274
273
|
}
|
|
274
|
+
static NAMING_CONVERTERS = {
|
|
275
|
+
kebab: toKebabCase,
|
|
276
|
+
camel: toCamelCase,
|
|
277
|
+
pascal: (name) => name,
|
|
278
|
+
};
|
|
275
279
|
/**
|
|
276
280
|
* Generate filename from component name using naming convention
|
|
277
281
|
*/
|
|
278
282
|
generateFileName(componentName, extension, namingConvention) {
|
|
279
|
-
|
|
280
|
-
const
|
|
281
|
-
kebab: toKebabCase,
|
|
282
|
-
camel: toCamelCase,
|
|
283
|
-
pascal: (name) => name,
|
|
284
|
-
};
|
|
285
|
-
const converter = namingConverters[namingConvention || 'pascal'];
|
|
283
|
+
const convention = namingConvention || 'pascal';
|
|
284
|
+
const converter = SVGProcessor.NAMING_CONVERTERS[convention];
|
|
286
285
|
const fileName = converter ? converter(componentName) : componentName;
|
|
287
286
|
return `${fileName}.${extension}`;
|
|
288
287
|
}
|
|
@@ -3,6 +3,7 @@ import { FileSystem } from '../utils/native.js';
|
|
|
3
3
|
import { logger } from '../core/logger.js';
|
|
4
4
|
import { configService } from './config.js';
|
|
5
5
|
import { svgProcessor } from '../processors/svg-processor.js';
|
|
6
|
+
import { frameworkTemplateEngine } from '../core/framework-templates.js';
|
|
6
7
|
import { fileWatcher } from './file-watcher.js';
|
|
7
8
|
import { OptLevel } from '../optimizers/types.js';
|
|
8
9
|
/**
|
|
@@ -261,12 +262,18 @@ export class SVGService {
|
|
|
261
262
|
*/
|
|
262
263
|
async handleFileRemoval(filePath, outDir, config) {
|
|
263
264
|
try {
|
|
264
|
-
|
|
265
|
+
// Get configuration
|
|
266
|
+
const fullConfig = configService.readConfig();
|
|
267
|
+
const mergedConfig = { ...fullConfig, ...config };
|
|
268
|
+
const namingConvention = mergedConfig.outputConfig?.naming || 'pascal';
|
|
269
|
+
const framework = mergedConfig.framework || 'react';
|
|
270
|
+
const typescript = mergedConfig.typescript !== false;
|
|
271
|
+
const extension = frameworkTemplateEngine.getFileExtension(framework, typescript);
|
|
265
272
|
const componentName = svgProcessor.generateComponentName(path.basename(filePath), namingConvention);
|
|
266
|
-
const componentPath = path.join(outDir, `${componentName}
|
|
273
|
+
const componentPath = path.join(outDir, `${componentName}.${extension}`);
|
|
267
274
|
if (await FileSystem.exists(componentPath)) {
|
|
268
275
|
await FileSystem.unlink(componentPath);
|
|
269
|
-
logger.success(`Removed component: ${componentName}
|
|
276
|
+
logger.success(`Removed component: ${componentName}.${extension}`);
|
|
270
277
|
}
|
|
271
278
|
}
|
|
272
279
|
catch (error) {
|
package/dist/watch.js
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import path from 'path';
|
|
2
2
|
import { generateSVG } from './builder.js';
|
|
3
3
|
import { isLocked } from './lock.js';
|
|
4
|
-
import { FileSystem, FileWatcher
|
|
4
|
+
import { FileSystem, FileWatcher } from './utils/native.js';
|
|
5
|
+
import { configService } from './services/config.js';
|
|
6
|
+
import { svgProcessor } from './processors/svg-processor.js';
|
|
7
|
+
import { frameworkTemplateEngine } from './core/framework-templates.js';
|
|
5
8
|
/**
|
|
6
9
|
* Watches a source folder for changes to SVG files and automatically
|
|
7
10
|
* rebuilds React components when SVGs are added, modified, or deleted.
|
|
@@ -59,13 +62,17 @@ export async function watchSVGs(config) {
|
|
|
59
62
|
await generateSVG({ svgFile: filePath, outDir });
|
|
60
63
|
}
|
|
61
64
|
else {
|
|
62
|
-
// File was deleted โ use
|
|
63
|
-
const
|
|
64
|
-
const
|
|
65
|
-
const
|
|
65
|
+
// File was deleted โ use configured naming convention and extension
|
|
66
|
+
const config = configService.readConfig();
|
|
67
|
+
const namingConvention = config.outputConfig?.naming || 'pascal';
|
|
68
|
+
const framework = config.framework || 'react';
|
|
69
|
+
const typescript = config.typescript !== false;
|
|
70
|
+
const extension = frameworkTemplateEngine.getFileExtension(framework, typescript);
|
|
71
|
+
const componentName = svgProcessor.generateComponentName(path.basename(filePath), namingConvention);
|
|
72
|
+
const outFile = path.join(outDir, `${componentName}.${extension}`);
|
|
66
73
|
if (await FileSystem.exists(outFile)) {
|
|
67
74
|
await FileSystem.unlink(outFile);
|
|
68
|
-
console.log(`๐๏ธ Removed component: ${componentName}
|
|
75
|
+
console.log(`๐๏ธ Removed component: ${componentName}.${extension}`);
|
|
69
76
|
}
|
|
70
77
|
}
|
|
71
78
|
});
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
**ADR** How to implement correctly SVG Integration Methods in React
|
|
2
|
+
|
|
3
|
+
**Version: 1.0.0**
|
|
4
|
+
**Date: 10/14/2025**
|
|
5
|
+
**Author: Engineer Navid Rezadoost**
|
|
6
|
+
**TDR Document ID: [TDR-SVG-INTRGRATION-METHODS-001](https://docs.google.com/document/d/1b04_V01xOvLiSMzuPdaRynANlnt2wYdJ_vjs9MAqtn4/edit?tab=t.0)**
|
|
7
|
+
**Status**: Proposed
|
|
8
|
+
|
|
9
|
+
**Context**
|
|
10
|
+
This service is responsible for managing svg files. Given the documentation provided in the TDR for implementing Method 3, which covers the weaknesses of svgr, it was decided to release this package as an open source service on npm.
|
|
11
|
+
|
|
12
|
+
**Similar Weaknesses That We Are Looking To Solve In This Process**
|
|
13
|
+
|
|
14
|
+
* Add more commands for more control and mastery over svg file management
|
|
15
|
+
* Add different commands for rendering and rebuilding svg components with more control over files that we do not want to be rebuilt
|
|
16
|
+
* Add a command to create a component for new files that are added
|
|
17
|
+
* A command to componentize a specific svg
|
|
18
|
+
* Add settings for files that should remain unchanged due to specific styling
|
|
19
|
+
* Introduce the folder and run the build as \--watch as soon as a new svg file is added to the specified folder, the svg will be built automatically
|
|
20
|
+
* Create a config file or command on the command line to specify the source file to monitor or process and specify the destination directory
|
|
21
|
+
* Specify rules for building components in width and height dimensions and even styles that should be set as default
|
|
22
|
+
|
|
23
|
+
#### **Decision**
|
|
24
|
+
|
|
25
|
+
We will develop a custom SVG integration CLI and runtime service for React that:
|
|
26
|
+
|
|
27
|
+
* Watches directories for new SVG files.
|
|
28
|
+
* Generates React components automatically with flexible configuration.
|
|
29
|
+
* Allows selective rebuilds and fine-grained control over output styles and props.
|
|
30
|
+
|
|
31
|
+
#### **Rationale**
|
|
32
|
+
|
|
33
|
+
SVGR, while popular, has the following shortcomings:
|
|
34
|
+
|
|
35
|
+
* Lacks a flexible CLI API for selective component generation.
|
|
36
|
+
* No built-in `--watch` mode for automatic builds on new file additions.
|
|
37
|
+
* Limited support for default dimension and styling rules.
|
|
38
|
+
* Difficult to exclude or lock specific SVGs from rebuilds.
|
|
39
|
+
|
|
40
|
+
#### **Consequences**
|
|
41
|
+
|
|
42
|
+
* More control and automation for developers.
|
|
43
|
+
* Slightly increased setup complexity.
|
|
44
|
+
* Responsibility to maintain a custom build tool.
|
|
45
|
+
|
|
46
|
+
## **Example Configuration and Commands**
|
|
47
|
+
|
|
48
|
+
**Include examples so developers can easily visualize the usage.**
|
|
49
|
+
|
|
50
|
+
### Example `.svgconfig.json`
|
|
51
|
+
|
|
52
|
+
| { "source": "./src/assets/svg", "output": "./src/components/icons", "watch": true, "defaultWidth": "24", "defaultHeight": "24", "defaultFill": "currentColor", "exclude": \["logo.svg", "brand-icon.svg"\], "styleRules": { "fill": "inherit", "stroke": "none" }} |
|
|
53
|
+
| :---- |
|
|
54
|
+
|
|
55
|
+
**Commands**
|
|
56
|
+
|
|
57
|
+
**Build Command**
|
|
58
|
+
Builds React components from all SVG files in the specified source directory.
|
|
59
|
+
|
|
60
|
+
| svg-tool build \[options\] |
|
|
61
|
+
| :---- |
|
|
62
|
+
|
|
63
|
+
| svg-tool build \--src ./src/assets/svg \--out ./src/components/icons |
|
|
64
|
+
| :---- |
|
|
65
|
+
|
|
66
|
+
### **What It Does**
|
|
67
|
+
|
|
68
|
+
* Converts every `.svg` in the source folder to a React component.
|
|
69
|
+
* Applies rules from `.svgconfig.json` if present.
|
|
70
|
+
* Generates file names based on kebab-case or PascalCase (configurable).
|
|
71
|
+
|
|
72
|
+
**Watch Command**
|
|
73
|
+
Continuously watches the source directory for new or updated SVG files, and automatically builds them.
|
|
74
|
+
|
|
75
|
+
| svg-tool watch \[options\] |
|
|
76
|
+
| :---- |
|
|
77
|
+
|
|
78
|
+
| svg-tool watch \--src ./src/assets/svg \--out ./src/components/icons |
|
|
79
|
+
| :---- |
|
|
80
|
+
|
|
81
|
+
### **What It Does**
|
|
82
|
+
|
|
83
|
+
* Automatically rebuilds when an SVG file is added, updated, or removed.
|
|
84
|
+
* Skips locked SVG files.
|
|
85
|
+
* Ideal for active development environments.
|
|
86
|
+
|
|
87
|
+
## **Generate Command**
|
|
88
|
+
|
|
89
|
+
Converts a specific SVG file into a React component on demand.
|
|
90
|
+
|
|
91
|
+
| svg-tool generate \<svgFile\> \[options\] |
|
|
92
|
+
| :---- |
|
|
93
|
+
|
|
94
|
+
| svg-tool generate ./src/assets/svg/heart.svg \--out ./src/components/icons |
|
|
95
|
+
| :---- |
|
|
96
|
+
|
|
97
|
+
### **What It Does**
|
|
98
|
+
|
|
99
|
+
* Converts only the specified file.
|
|
100
|
+
* Ideal for adding individual icons to an existing collection.
|
|
101
|
+
|
|
102
|
+
## **Lock Command**
|
|
103
|
+
|
|
104
|
+
Locks one or more SVG files to prevent them from being rebuilt or overwritten during batch operations.
|
|
105
|
+
|
|
106
|
+
| svg-tool lock \<svgFile\> \[options\] |
|
|
107
|
+
| :---- |
|
|
108
|
+
|
|
109
|
+
| svg-tool lock ./src/assets/svg/logo.svg |
|
|
110
|
+
| :---- |
|
|
111
|
+
|
|
112
|
+
### **What It Does**
|
|
113
|
+
|
|
114
|
+
* Adds the specified file(s) to a `.svg-lock` or `.svgconfig.json` list.
|
|
115
|
+
* Protects specific SVGs with custom branding or styling.
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
## **Unlock Command**
|
|
119
|
+
|
|
120
|
+
Unlocks one or more SVG files previously marked as locked, allowing them to be rebuilt.
|
|
121
|
+
|
|
122
|
+
| svg-tool unlock \<svgFile\> \[options\] |
|
|
123
|
+
| :---- |
|
|
124
|
+
|
|
125
|
+
| svg-tool unlock ./src/assets/svg/logo.svg |
|
|
126
|
+
| :---- |
|
|
127
|
+
|
|
128
|
+
## **Config Command**
|
|
129
|
+
|
|
130
|
+
Creates or modifies a `.svgconfig.json` file with default settings for builds and watches.
|
|
131
|
+
|
|
132
|
+
| svg-tool config \[options\] |
|
|
133
|
+
| :---- |
|
|
134
|
+
|
|
135
|
+
| svg-tool config \--init |
|
|
136
|
+
| :---- |
|
|
137
|
+
|
|
138
|
+
**\--init**
|
|
139
|
+
|
|
140
|
+
| svg-tool config \--init |
|
|
141
|
+
| :---- |
|
|
142
|
+
|
|
143
|
+
**\--set \<key=value\>**
|
|
144
|
+
|
|
145
|
+
| svg-tool config \--set defaultWidth=32 |
|
|
146
|
+
| :---- |
|
|
147
|
+
|
|
148
|
+
**`--show`**
|
|
149
|
+
|
|
150
|
+
| svg-tool config \--show |
|
|
151
|
+
| :---- |
|
|
152
|
+
|
|
153
|
+
**`Full One-Line Command Closet (for Bash / Mac / Linux / PowerShell)`**
|
|
154
|
+
|
|
155
|
+
| svg-tool config \--init && svg-tool config \--set source=./src/assets/svg && svg-tool config \--set output=./src/components/icons && svg-tool config \--set watch=true && svg-tool config \--set defaultWidth=24 && svg-tool config \--set defaultHeight=24 && svg-tool config \--set defaultFill=currentColor && svg-tool lock ./src/assets/svg/logo.svg && svg-tool build \--src ./src/assets/svg \--out ./src/components/icons \--verbose && svg-tool generate ./src/assets/svg/new-icon.svg \--out ./src/components/icons && svg-tool unlock ./src/assets/svg/logo.svg && svg-tool watch \--src ./src/assets/svg \--out ./src/components/icons && svg-tool clean \--out ./src/components/icons |
|
|
156
|
+
| :---- |
|
|
157
|
+
|