svger-cli 2.0.2 → 2.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.svgerconfig.example.json +119 -0
- package/CHANGELOG.md +64 -0
- package/DEVELOPMENT.md +353 -0
- package/README.md +430 -209
- package/SECURITY.md +69 -0
- package/dist/builder.js +16 -16
- package/dist/clean.js +2 -2
- package/dist/cli.js +38 -38
- package/dist/config.js +95 -12
- package/dist/core/error-handler.js +12 -9
- package/dist/core/framework-templates.js +5 -3
- package/dist/core/performance-engine.js +9 -8
- package/dist/core/plugin-manager.js +7 -5
- package/dist/core/style-compiler.js +17 -15
- package/dist/core/template-manager.js +14 -14
- package/dist/index.d.ts +8 -6
- package/dist/index.js +5 -5
- package/dist/lock.js +7 -7
- package/dist/processors/svg-processor.d.ts +9 -3
- package/dist/processors/svg-processor.js +56 -18
- package/dist/services/config.js +72 -20
- package/dist/services/file-watcher.js +3 -3
- package/dist/services/svg-service.js +34 -30
- package/dist/templates/ComponentTemplate.js +25 -25
- package/dist/types/index.d.ts +77 -19
- package/dist/utils/native.d.ts +32 -1
- package/dist/utils/native.js +47 -8
- package/dist/watch.d.ts +1 -1
- package/dist/watch.js +14 -14
- package/docs/ADR-SVG-INTRGRATION-METHODS-001.adr.md +157 -0
- package/docs/ADR-SVG-INTRGRATION-METHODS-002.adr.md +550 -0
- package/docs/FRAMEWORK-GUIDE.md +768 -0
- package/docs/IMPLEMENTATION-SUMMARY.md +376 -0
- package/docs/TDR-SVG-INTRGRATION-METHODS-001.tdr.md +115 -0
- package/package.json +155 -14
package/SECURITY.md
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# Security Policy
|
|
2
|
+
|
|
3
|
+
## Supported Versions
|
|
4
|
+
|
|
5
|
+
We actively support the following versions of SVGER-CLI with security updates:
|
|
6
|
+
|
|
7
|
+
| Version | Supported |
|
|
8
|
+
| ------- | ------------------ |
|
|
9
|
+
| 2.0.x | :white_check_mark: |
|
|
10
|
+
| 1.x.x | :x: |
|
|
11
|
+
|
|
12
|
+
## Reporting a Vulnerability
|
|
13
|
+
|
|
14
|
+
We take the security of SVGER-CLI seriously. If you discover a security vulnerability, please follow these guidelines:
|
|
15
|
+
|
|
16
|
+
### How to Report
|
|
17
|
+
|
|
18
|
+
1. **DO NOT** create a public GitHub issue for security vulnerabilities
|
|
19
|
+
2. Send an email to **faezemohades@gmail.com** with the subject line: `[SECURITY] SVGER-CLI Vulnerability Report`
|
|
20
|
+
3. Include the following information:
|
|
21
|
+
- Description of the vulnerability
|
|
22
|
+
- Steps to reproduce the issue
|
|
23
|
+
- Potential impact
|
|
24
|
+
- Suggested fix (if any)
|
|
25
|
+
- Your contact information
|
|
26
|
+
|
|
27
|
+
### What to Expect
|
|
28
|
+
|
|
29
|
+
- **Acknowledgment**: We will acknowledge receipt of your report within 24 hours
|
|
30
|
+
- **Assessment**: We will assess the vulnerability within 72 hours
|
|
31
|
+
- **Updates**: We will provide regular updates on our progress
|
|
32
|
+
- **Resolution**: Critical vulnerabilities will be patched within 7 days, others within 30 days
|
|
33
|
+
|
|
34
|
+
### Security Best Practices for Users
|
|
35
|
+
|
|
36
|
+
When using SVGER-CLI in your projects:
|
|
37
|
+
|
|
38
|
+
1. **Keep Updated**: Always use the latest version
|
|
39
|
+
2. **Validate Input**: Ensure SVG files come from trusted sources
|
|
40
|
+
3. **Review Output**: Inspect generated components before deploying
|
|
41
|
+
4. **File Permissions**: Use appropriate file permissions for generated components
|
|
42
|
+
5. **CI/CD Security**: Secure your build pipelines that use SVGER-CLI
|
|
43
|
+
|
|
44
|
+
### Security Features
|
|
45
|
+
|
|
46
|
+
SVGER-CLI includes several security features:
|
|
47
|
+
|
|
48
|
+
- **Zero Dependencies**: Eliminates third-party vulnerability vectors
|
|
49
|
+
- **Input Validation**: Validates SVG content before processing
|
|
50
|
+
- **Sandboxed Processing**: Processes files in isolated contexts
|
|
51
|
+
- **Safe Output Generation**: Generates safe, sanitized component code
|
|
52
|
+
- **File Locking**: Prevents unauthorized modification of protected files
|
|
53
|
+
|
|
54
|
+
### Disclosure Policy
|
|
55
|
+
|
|
56
|
+
- We believe in responsible disclosure
|
|
57
|
+
- We will credit security researchers who report vulnerabilities responsibly
|
|
58
|
+
- We may create a security advisory for significant vulnerabilities
|
|
59
|
+
- We will notify users through our release notes and GitHub security advisories
|
|
60
|
+
|
|
61
|
+
## Contact
|
|
62
|
+
|
|
63
|
+
For any security-related questions or concerns:
|
|
64
|
+
|
|
65
|
+
- **Primary Contact**: faezemohades@gmail.com
|
|
66
|
+
- **Alternative Contact**: navidrezadoost07@gmail.com
|
|
67
|
+
- **PGP Key**: Available upon request
|
|
68
|
+
|
|
69
|
+
Thank you for helping keep SVGER-CLI secure!
|
package/dist/builder.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import path from
|
|
2
|
-
import { toPascalCase, FileSystem } from
|
|
3
|
-
import { isLocked } from
|
|
4
|
-
import { readConfig } from
|
|
5
|
-
import { reactTemplate } from
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import { toPascalCase, FileSystem } from './utils/native.js';
|
|
3
|
+
import { isLocked } from './lock.js';
|
|
4
|
+
import { readConfig } from './config.js';
|
|
5
|
+
import { reactTemplate } from './templates/ComponentTemplate.js';
|
|
6
6
|
/**
|
|
7
7
|
* Converts all SVG files from a source directory into React components and writes them to an output directory.
|
|
8
8
|
*
|
|
@@ -16,13 +16,13 @@ export async function buildAll(config) {
|
|
|
16
16
|
const srcDir = path.resolve(config.src);
|
|
17
17
|
const outDir = path.resolve(config.out);
|
|
18
18
|
if (!(await FileSystem.exists(srcDir))) {
|
|
19
|
-
console.error(
|
|
19
|
+
console.error('❌ Source folder not found:', srcDir);
|
|
20
20
|
process.exit(1);
|
|
21
21
|
}
|
|
22
22
|
await FileSystem.ensureDir(outDir);
|
|
23
|
-
const files = (await FileSystem.readDir(srcDir)).filter((f) => f.endsWith(
|
|
23
|
+
const files = (await FileSystem.readDir(srcDir)).filter((f) => f.endsWith('.svg'));
|
|
24
24
|
if (!files.length) {
|
|
25
|
-
console.log(
|
|
25
|
+
console.log('⚠️ No SVG files found in', srcDir);
|
|
26
26
|
return;
|
|
27
27
|
}
|
|
28
28
|
for (const file of files) {
|
|
@@ -31,8 +31,8 @@ export async function buildAll(config) {
|
|
|
31
31
|
console.log(`⚠️ Skipped locked file: ${file}`);
|
|
32
32
|
continue;
|
|
33
33
|
}
|
|
34
|
-
const svgContent = await FileSystem.readFile(svgPath,
|
|
35
|
-
const componentName = toPascalCase(file.replace(
|
|
34
|
+
const svgContent = await FileSystem.readFile(svgPath, 'utf-8');
|
|
35
|
+
const componentName = toPascalCase(file.replace('.svg', ''));
|
|
36
36
|
const componentCode = reactTemplate({
|
|
37
37
|
componentName,
|
|
38
38
|
svgContent,
|
|
@@ -41,10 +41,10 @@ export async function buildAll(config) {
|
|
|
41
41
|
defaultFill: svgConfig.defaultFill,
|
|
42
42
|
});
|
|
43
43
|
const outFile = path.join(outDir, `${componentName}.tsx`);
|
|
44
|
-
await FileSystem.writeFile(outFile, componentCode,
|
|
44
|
+
await FileSystem.writeFile(outFile, componentCode, 'utf-8');
|
|
45
45
|
console.log(`✅ Generated: ${componentName}.tsx`);
|
|
46
46
|
}
|
|
47
|
-
console.log(
|
|
47
|
+
console.log('🎉 All SVGs have been converted successfully!');
|
|
48
48
|
}
|
|
49
49
|
/**
|
|
50
50
|
* Generates a single React component from an SVG file.
|
|
@@ -62,11 +62,11 @@ export async function generateSVG({ svgFile, outDir, }) {
|
|
|
62
62
|
return;
|
|
63
63
|
}
|
|
64
64
|
if (!(await FileSystem.exists(filePath))) {
|
|
65
|
-
console.error(
|
|
65
|
+
console.error('❌ SVG file not found:', filePath);
|
|
66
66
|
process.exit(1);
|
|
67
67
|
}
|
|
68
|
-
const svgContent = await FileSystem.readFile(filePath,
|
|
69
|
-
const componentName = toPascalCase(path.basename(svgFile,
|
|
68
|
+
const svgContent = await FileSystem.readFile(filePath, 'utf-8');
|
|
69
|
+
const componentName = toPascalCase(path.basename(svgFile, '.svg'));
|
|
70
70
|
const componentCode = reactTemplate({
|
|
71
71
|
componentName,
|
|
72
72
|
svgContent,
|
|
@@ -77,6 +77,6 @@ export async function generateSVG({ svgFile, outDir, }) {
|
|
|
77
77
|
const outputFolder = path.resolve(outDir);
|
|
78
78
|
await FileSystem.ensureDir(outputFolder);
|
|
79
79
|
const outFile = path.join(outputFolder, `${componentName}.tsx`);
|
|
80
|
-
await FileSystem.writeFile(outFile, componentCode,
|
|
80
|
+
await FileSystem.writeFile(outFile, componentCode, 'utf-8');
|
|
81
81
|
console.log(`✅ Generated: ${componentName}.tsx`);
|
|
82
82
|
}
|
package/dist/clean.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import path from
|
|
2
|
-
import { FileSystem } from
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import { FileSystem } from './utils/native.js';
|
|
3
3
|
/**
|
|
4
4
|
* Cleans the specified output directory by removing all files and folders inside it.
|
|
5
5
|
* Typically used to clear previously generated SVG React components before a new build.
|
package/dist/cli.js
CHANGED
|
@@ -1,30 +1,30 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { CLI } from
|
|
3
|
-
import { svgService } from
|
|
4
|
-
import { configService } from
|
|
5
|
-
import { logger } from
|
|
2
|
+
import { CLI } from './utils/native.js';
|
|
3
|
+
import { svgService } from './services/svg-service.js';
|
|
4
|
+
import { configService } from './services/config.js';
|
|
5
|
+
import { logger } from './core/logger.js';
|
|
6
6
|
const program = new CLI();
|
|
7
7
|
/**
|
|
8
8
|
* svger-cli CLI
|
|
9
9
|
* Custom SVG to Angular, React, Vue, Svelte, Solid, and other component converter.
|
|
10
10
|
*/
|
|
11
11
|
program
|
|
12
|
-
.name(
|
|
13
|
-
.description(
|
|
14
|
-
.version(
|
|
12
|
+
.name('svger-cli')
|
|
13
|
+
.description('Custom SVG to Angular, React, Vue, Svelte, Solid, and other component converter')
|
|
14
|
+
.version('2.0.0');
|
|
15
15
|
// -------- Build Command --------
|
|
16
16
|
/**
|
|
17
17
|
* Build all SVGs from a source folder to an output folder.
|
|
18
18
|
*/
|
|
19
19
|
program
|
|
20
|
-
.command(
|
|
21
|
-
.description(
|
|
22
|
-
.option(
|
|
23
|
-
.option(
|
|
24
|
-
.option(
|
|
25
|
-
.option(
|
|
26
|
-
.option(
|
|
27
|
-
.option(
|
|
20
|
+
.command('build <src> <out>')
|
|
21
|
+
.description('Build all SVGs from source to output')
|
|
22
|
+
.option('--framework <type>', 'Target framework (react|vue|svelte|angular|solid|preact|lit|vanilla)')
|
|
23
|
+
.option('--typescript', 'Generate TypeScript components (default: true)')
|
|
24
|
+
.option('--no-typescript', 'Generate JavaScript components')
|
|
25
|
+
.option('--composition', 'Use Vue Composition API with <script setup>')
|
|
26
|
+
.option('--standalone', 'Generate Angular standalone components')
|
|
27
|
+
.option('--signals', 'Use Angular signals for reactive state')
|
|
28
28
|
.action(async (args, opts) => {
|
|
29
29
|
try {
|
|
30
30
|
const [src, out] = args;
|
|
@@ -62,8 +62,8 @@ program
|
|
|
62
62
|
* Watch a source folder and rebuild SVGs automatically on changes.
|
|
63
63
|
*/
|
|
64
64
|
program
|
|
65
|
-
.command(
|
|
66
|
-
.description(
|
|
65
|
+
.command('watch <src> <out>')
|
|
66
|
+
.description('Watch source folder and rebuild SVGs automatically')
|
|
67
67
|
.action(async (args) => {
|
|
68
68
|
try {
|
|
69
69
|
const [src, out] = args;
|
|
@@ -85,13 +85,13 @@ program
|
|
|
85
85
|
* Generate a component from a single SVG file.
|
|
86
86
|
*/
|
|
87
87
|
program
|
|
88
|
-
.command(
|
|
89
|
-
.description(
|
|
90
|
-
.option(
|
|
91
|
-
.option(
|
|
92
|
-
.option(
|
|
93
|
-
.option(
|
|
94
|
-
.option(
|
|
88
|
+
.command('generate <svgFile> <out>')
|
|
89
|
+
.description('Convert a single SVG file into a component')
|
|
90
|
+
.option('--framework <type>', 'Target framework (react|vue|svelte|angular|solid|preact|lit|vanilla)')
|
|
91
|
+
.option('--typescript', 'Generate TypeScript component (default: true)')
|
|
92
|
+
.option('--no-typescript', 'Generate JavaScript component')
|
|
93
|
+
.option('--composition', 'Use Vue Composition API with <script setup>')
|
|
94
|
+
.option('--standalone', 'Generate Angular standalone component')
|
|
95
95
|
.action(async (args, opts) => {
|
|
96
96
|
try {
|
|
97
97
|
const [svgFile, out] = args;
|
|
@@ -124,8 +124,8 @@ program
|
|
|
124
124
|
* Lock one or more SVG files to prevent accidental overwrites.
|
|
125
125
|
*/
|
|
126
126
|
program
|
|
127
|
-
.command(
|
|
128
|
-
.description(
|
|
127
|
+
.command('lock <files...>')
|
|
128
|
+
.description('Lock one or more SVG files')
|
|
129
129
|
.action((args) => {
|
|
130
130
|
try {
|
|
131
131
|
svgService.lockService.lockFiles(args);
|
|
@@ -139,8 +139,8 @@ program
|
|
|
139
139
|
* Unlock one or more SVG files to allow modifications.
|
|
140
140
|
*/
|
|
141
141
|
program
|
|
142
|
-
.command(
|
|
143
|
-
.description(
|
|
142
|
+
.command('unlock <files...>')
|
|
143
|
+
.description('Unlock one or more SVG files')
|
|
144
144
|
.action((args) => {
|
|
145
145
|
try {
|
|
146
146
|
svgService.lockService.unlockFiles(args);
|
|
@@ -155,19 +155,19 @@ program
|
|
|
155
155
|
* Manage svger-cli configuration.
|
|
156
156
|
*/
|
|
157
157
|
program
|
|
158
|
-
.command(
|
|
159
|
-
.description(
|
|
160
|
-
.option(
|
|
161
|
-
.option(
|
|
162
|
-
.option(
|
|
158
|
+
.command('config')
|
|
159
|
+
.description('Manage svger-cli configuration')
|
|
160
|
+
.option('--init', 'Create default .svgconfig.json')
|
|
161
|
+
.option('--set <keyValue>', 'Set config key=value')
|
|
162
|
+
.option('--show', 'Show current config')
|
|
163
163
|
.action(async (args, opts) => {
|
|
164
164
|
try {
|
|
165
165
|
if (opts.init)
|
|
166
166
|
return await configService.initConfig();
|
|
167
167
|
if (opts.set) {
|
|
168
|
-
const [key, value] = opts.set.split(
|
|
168
|
+
const [key, value] = opts.set.split('=');
|
|
169
169
|
if (!key || value === undefined) {
|
|
170
|
-
logger.error(
|
|
170
|
+
logger.error('Invalid format. Use key=value');
|
|
171
171
|
process.exit(1);
|
|
172
172
|
}
|
|
173
173
|
const parsedValue = !isNaN(Number(value)) ? Number(value) : value;
|
|
@@ -175,7 +175,7 @@ program
|
|
|
175
175
|
}
|
|
176
176
|
if (opts.show)
|
|
177
177
|
return configService.showConfig();
|
|
178
|
-
logger.error(
|
|
178
|
+
logger.error('No option provided. Use --init, --set, or --show');
|
|
179
179
|
}
|
|
180
180
|
catch (error) {
|
|
181
181
|
logger.error('Config operation failed:', error);
|
|
@@ -187,8 +187,8 @@ program
|
|
|
187
187
|
* Remove all generated SVG React components from an output folder.
|
|
188
188
|
*/
|
|
189
189
|
program
|
|
190
|
-
.command(
|
|
191
|
-
.description(
|
|
190
|
+
.command('clean <out>')
|
|
191
|
+
.description('Remove all generated SVG React components from output folder')
|
|
192
192
|
.action(async (args) => {
|
|
193
193
|
try {
|
|
194
194
|
const [out] = args;
|
package/dist/config.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import path from
|
|
2
|
-
import { FileSystem } from
|
|
3
|
-
const CONFIG_FILE =
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import { FileSystem } from './utils/native.js';
|
|
3
|
+
const CONFIG_FILE = '.svgconfig.json';
|
|
4
4
|
/**
|
|
5
5
|
* Get the absolute path to the configuration file.
|
|
6
6
|
*
|
|
@@ -31,24 +31,107 @@ export function writeConfig(config) {
|
|
|
31
31
|
*/
|
|
32
32
|
export async function initConfig() {
|
|
33
33
|
if (await FileSystem.exists(getConfigPath())) {
|
|
34
|
-
console.log(
|
|
34
|
+
console.log('⚠️ Config file already exists:', getConfigPath());
|
|
35
35
|
return;
|
|
36
36
|
}
|
|
37
37
|
const defaultConfig = {
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
// Source & Output
|
|
39
|
+
source: './src/assets/svg',
|
|
40
|
+
output: './src/components/icons',
|
|
41
|
+
// Framework Configuration
|
|
42
|
+
framework: 'react',
|
|
43
|
+
typescript: true,
|
|
44
|
+
componentType: 'functional',
|
|
45
|
+
// Processing Options
|
|
40
46
|
watch: false,
|
|
47
|
+
parallel: true,
|
|
48
|
+
batchSize: 10,
|
|
49
|
+
maxConcurrency: 4,
|
|
50
|
+
cache: true,
|
|
51
|
+
// Default Properties
|
|
41
52
|
defaultWidth: 24,
|
|
42
53
|
defaultHeight: 24,
|
|
43
|
-
defaultFill:
|
|
44
|
-
|
|
54
|
+
defaultFill: 'currentColor',
|
|
55
|
+
defaultStroke: 'none',
|
|
56
|
+
defaultStrokeWidth: 1,
|
|
57
|
+
// Styling Configuration
|
|
45
58
|
styleRules: {
|
|
46
|
-
fill:
|
|
47
|
-
stroke:
|
|
59
|
+
fill: 'inherit',
|
|
60
|
+
stroke: 'none',
|
|
61
|
+
},
|
|
62
|
+
responsive: {
|
|
63
|
+
breakpoints: ['sm', 'md', 'lg', 'xl'],
|
|
64
|
+
values: {
|
|
65
|
+
width: ['16px', '20px', '24px', '32px'],
|
|
66
|
+
height: ['16px', '20px', '24px', '32px'],
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
theme: {
|
|
70
|
+
mode: 'auto',
|
|
71
|
+
variables: {
|
|
72
|
+
primary: 'currentColor',
|
|
73
|
+
secondary: '#6b7280',
|
|
74
|
+
accent: '#3b82f6',
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
animations: [],
|
|
78
|
+
// Advanced Options
|
|
79
|
+
plugins: [],
|
|
80
|
+
exclude: [],
|
|
81
|
+
include: [],
|
|
82
|
+
// Error Handling
|
|
83
|
+
errorHandling: {
|
|
84
|
+
strategy: 'continue',
|
|
85
|
+
maxRetries: 3,
|
|
86
|
+
timeout: 30000,
|
|
87
|
+
},
|
|
88
|
+
// Performance Settings
|
|
89
|
+
performance: {
|
|
90
|
+
optimization: 'balanced',
|
|
91
|
+
memoryLimit: 512,
|
|
92
|
+
cacheTimeout: 3600000,
|
|
93
|
+
},
|
|
94
|
+
// Output Customization
|
|
95
|
+
outputConfig: {
|
|
96
|
+
naming: 'pascal',
|
|
97
|
+
extension: 'tsx',
|
|
98
|
+
directory: './src/components/icons',
|
|
99
|
+
},
|
|
100
|
+
// Framework-specific configurations
|
|
101
|
+
react: {
|
|
102
|
+
componentType: 'functional',
|
|
103
|
+
forwardRef: true,
|
|
104
|
+
memo: false,
|
|
105
|
+
propsInterface: 'SVGProps',
|
|
106
|
+
styledComponents: false,
|
|
107
|
+
cssModules: false,
|
|
108
|
+
},
|
|
109
|
+
vue: {
|
|
110
|
+
api: 'composition',
|
|
111
|
+
setup: true,
|
|
112
|
+
typescript: true,
|
|
113
|
+
scoped: true,
|
|
114
|
+
cssVariables: true,
|
|
115
|
+
},
|
|
116
|
+
angular: {
|
|
117
|
+
standalone: true,
|
|
118
|
+
signals: true,
|
|
119
|
+
changeDetection: 'OnPush',
|
|
120
|
+
encapsulation: 'Emulated',
|
|
121
|
+
},
|
|
122
|
+
// Legacy support (deprecated)
|
|
123
|
+
template: {
|
|
124
|
+
type: 'default',
|
|
125
|
+
},
|
|
126
|
+
frameworkOptions: {
|
|
127
|
+
forwardRef: true,
|
|
128
|
+
memo: false,
|
|
129
|
+
scriptSetup: true,
|
|
130
|
+
standalone: true,
|
|
48
131
|
},
|
|
49
132
|
};
|
|
50
133
|
writeConfig(defaultConfig);
|
|
51
|
-
console.log(
|
|
134
|
+
console.log('✅ Config file created:', getConfigPath());
|
|
52
135
|
}
|
|
53
136
|
/**
|
|
54
137
|
* Set a specific configuration key to a new value.
|
|
@@ -67,6 +150,6 @@ export function setConfig(key, value) {
|
|
|
67
150
|
*/
|
|
68
151
|
export function showConfig() {
|
|
69
152
|
const config = readConfig();
|
|
70
|
-
console.log(
|
|
153
|
+
console.log('📄 Current Config:');
|
|
71
154
|
console.log(JSON.stringify(config, null, 2));
|
|
72
155
|
}
|
|
@@ -47,7 +47,7 @@ export class SVGErrorHandler {
|
|
|
47
47
|
total: this.errorHistory.length,
|
|
48
48
|
bySeverity,
|
|
49
49
|
byCode,
|
|
50
|
-
recentErrors: this.errorHistory.slice(-10)
|
|
50
|
+
recentErrors: this.errorHistory.slice(-10),
|
|
51
51
|
};
|
|
52
52
|
}
|
|
53
53
|
/**
|
|
@@ -70,7 +70,7 @@ export class SVGErrorHandler {
|
|
|
70
70
|
severity: this.determineSeverity(regularError),
|
|
71
71
|
context: context || {},
|
|
72
72
|
timestamp: Date.now(),
|
|
73
|
-
stack: regularError.stack
|
|
73
|
+
stack: regularError.stack,
|
|
74
74
|
};
|
|
75
75
|
}
|
|
76
76
|
categorizeError(error) {
|
|
@@ -156,15 +156,15 @@ export class SVGErrorHandler {
|
|
|
156
156
|
setupDefaultStrategies() {
|
|
157
157
|
// File not found recovery
|
|
158
158
|
this.registerRecoveryStrategy('FILE_NOT_FOUND', {
|
|
159
|
-
canRecover:
|
|
159
|
+
canRecover: error => error.context?.filePath && error.context?.canSkip === true,
|
|
160
160
|
recover: async (error, context) => {
|
|
161
161
|
logger.warn(`Skipping missing file: ${error.context?.filePath}`);
|
|
162
162
|
return { skipped: true, filePath: error.context?.filePath };
|
|
163
|
-
}
|
|
163
|
+
},
|
|
164
164
|
});
|
|
165
165
|
// Invalid SVG recovery
|
|
166
166
|
this.registerRecoveryStrategy('INVALID_SVG', {
|
|
167
|
-
canRecover:
|
|
167
|
+
canRecover: error => error.context?.svgContent,
|
|
168
168
|
recover: async (error, context) => {
|
|
169
169
|
logger.info('Attempting to clean invalid SVG content');
|
|
170
170
|
// Basic SVG cleanup
|
|
@@ -176,15 +176,15 @@ export class SVGErrorHandler {
|
|
|
176
176
|
.replace(/on\w+="[^"]*"/gi, '') // Remove event handlers
|
|
177
177
|
.replace(/javascript:[^"']*/gi, ''); // Remove javascript: URLs
|
|
178
178
|
return { cleanedContent: cleaned };
|
|
179
|
-
}
|
|
179
|
+
},
|
|
180
180
|
});
|
|
181
181
|
// Permission denied recovery
|
|
182
182
|
this.registerRecoveryStrategy('PERMISSION_DENIED', {
|
|
183
|
-
canRecover:
|
|
183
|
+
canRecover: error => error.context?.alternative,
|
|
184
184
|
recover: async (error, context) => {
|
|
185
185
|
logger.warn(`Using alternative path due to permission issue: ${error.context?.alternative}`);
|
|
186
186
|
return { alternativePath: error.context?.alternative };
|
|
187
|
-
}
|
|
187
|
+
},
|
|
188
188
|
});
|
|
189
189
|
logger.debug('Default error recovery strategies loaded');
|
|
190
190
|
}
|
|
@@ -218,7 +218,10 @@ export function handleErrors(context) {
|
|
|
218
218
|
return function (target, propertyName, descriptor) {
|
|
219
219
|
const method = descriptor.value;
|
|
220
220
|
descriptor.value = async function (...args) {
|
|
221
|
-
return withErrorHandling(() => method.apply(this, args), {
|
|
221
|
+
return withErrorHandling(() => method.apply(this, args), {
|
|
222
|
+
method: propertyName,
|
|
223
|
+
...context,
|
|
224
|
+
});
|
|
222
225
|
};
|
|
223
226
|
};
|
|
224
227
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export class FrameworkTemplateEngine {
|
|
2
2
|
generateComponent(options) {
|
|
3
|
-
const { framework, componentName, svgContent, typescript = true, frameworkOptions = {} } = options;
|
|
3
|
+
const { framework, componentName, svgContent, typescript = true, frameworkOptions = {}, } = options;
|
|
4
4
|
switch (framework) {
|
|
5
5
|
case 'react':
|
|
6
6
|
return this.generateReactComponent(componentName, svgContent, typescript, frameworkOptions);
|
|
@@ -215,8 +215,10 @@ export default defineComponent({
|
|
|
215
215
|
>
|
|
216
216
|
${innerContent}
|
|
217
217
|
</svg>
|
|
218
|
-
\`,${standalone
|
|
219
|
-
|
|
218
|
+
\`,${standalone
|
|
219
|
+
? `
|
|
220
|
+
changeDetection: ChangeDetectionStrategy.OnPush`
|
|
221
|
+
: ''}
|
|
220
222
|
})
|
|
221
223
|
export class ${componentName}Component {
|
|
222
224
|
@Input() className: string = '';
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import os from 'os';
|
|
1
2
|
import { logger } from '../core/logger.js';
|
|
2
3
|
import { svgProcessor } from '../processors/svg-processor.js';
|
|
3
4
|
/**
|
|
@@ -18,7 +19,7 @@ export class PerformanceEngine {
|
|
|
18
19
|
* Process multiple SVG files in parallel with optimized batching
|
|
19
20
|
*/
|
|
20
21
|
async processBatch(files, config = {}) {
|
|
21
|
-
const { batchSize = 10, parallel = true, maxConcurrency = Math.min(4,
|
|
22
|
+
const { batchSize = 10, parallel = true, maxConcurrency = Math.min(4, os.cpus().length), } = config;
|
|
22
23
|
logger.info(`Processing ${files.length} files with ${parallel ? 'parallel' : 'sequential'} execution`);
|
|
23
24
|
const startTime = Date.now();
|
|
24
25
|
const results = [];
|
|
@@ -57,7 +58,7 @@ export class PerformanceEngine {
|
|
|
57
58
|
return {
|
|
58
59
|
success: true,
|
|
59
60
|
filePath: file.path,
|
|
60
|
-
duration: Date.now() - startTime
|
|
61
|
+
duration: Date.now() - startTime,
|
|
61
62
|
};
|
|
62
63
|
}
|
|
63
64
|
try {
|
|
@@ -70,7 +71,7 @@ export class PerformanceEngine {
|
|
|
70
71
|
success: result.success,
|
|
71
72
|
filePath: file.path,
|
|
72
73
|
error: result.error,
|
|
73
|
-
duration: Date.now() - startTime
|
|
74
|
+
duration: Date.now() - startTime,
|
|
74
75
|
};
|
|
75
76
|
}
|
|
76
77
|
catch (error) {
|
|
@@ -78,7 +79,7 @@ export class PerformanceEngine {
|
|
|
78
79
|
success: false,
|
|
79
80
|
filePath: file.path,
|
|
80
81
|
error: error,
|
|
81
|
-
duration: Date.now() - startTime
|
|
82
|
+
duration: Date.now() - startTime,
|
|
82
83
|
};
|
|
83
84
|
}
|
|
84
85
|
}
|
|
@@ -131,7 +132,7 @@ export class PerformanceEngine {
|
|
|
131
132
|
heapTotal: heapTotalMB,
|
|
132
133
|
external: memUsage.external / 1024 / 1024,
|
|
133
134
|
cacheSize,
|
|
134
|
-
recommendations
|
|
135
|
+
recommendations,
|
|
135
136
|
};
|
|
136
137
|
}
|
|
137
138
|
/**
|
|
@@ -149,7 +150,7 @@ export class PerformanceEngine {
|
|
|
149
150
|
return {
|
|
150
151
|
cacheHitRate: 0, // Placeholder
|
|
151
152
|
averageProcessingTime: 0, // Placeholder
|
|
152
|
-
memoryUsage: this.monitorMemoryUsage()
|
|
153
|
+
memoryUsage: this.monitorMemoryUsage(),
|
|
153
154
|
};
|
|
154
155
|
}
|
|
155
156
|
// Private helper methods
|
|
@@ -188,7 +189,7 @@ export class PerformanceEngine {
|
|
|
188
189
|
setCachedResult(key, result) {
|
|
189
190
|
this.processingCache.set(key, {
|
|
190
191
|
result,
|
|
191
|
-
timestamp: Date.now()
|
|
192
|
+
timestamp: Date.now(),
|
|
192
193
|
});
|
|
193
194
|
}
|
|
194
195
|
applyFastOptimizations(content) {
|
|
@@ -234,7 +235,7 @@ class Semaphore {
|
|
|
234
235
|
this.permits--;
|
|
235
236
|
return Promise.resolve();
|
|
236
237
|
}
|
|
237
|
-
return new Promise(
|
|
238
|
+
return new Promise(resolve => {
|
|
238
239
|
this.waitQueue.push(resolve);
|
|
239
240
|
});
|
|
240
241
|
}
|
|
@@ -26,7 +26,7 @@ export class PluginManager {
|
|
|
26
26
|
process: async (content, options) => {
|
|
27
27
|
return this.optimizeSVG(content, options);
|
|
28
28
|
},
|
|
29
|
-
validate: (options) => true
|
|
29
|
+
validate: (options) => true,
|
|
30
30
|
});
|
|
31
31
|
// Color Theme Plugin
|
|
32
32
|
this.registerPlugin({
|
|
@@ -37,7 +37,7 @@ export class PluginManager {
|
|
|
37
37
|
},
|
|
38
38
|
validate: (options) => {
|
|
39
39
|
return options && typeof options.theme === 'object';
|
|
40
|
-
}
|
|
40
|
+
},
|
|
41
41
|
});
|
|
42
42
|
// Size Normalizer Plugin
|
|
43
43
|
this.registerPlugin({
|
|
@@ -45,7 +45,7 @@ export class PluginManager {
|
|
|
45
45
|
version: '1.0.0',
|
|
46
46
|
process: async (content, options) => {
|
|
47
47
|
return this.normalizeSizes(content, options);
|
|
48
|
-
}
|
|
48
|
+
},
|
|
49
49
|
});
|
|
50
50
|
logger.debug('Built-in plugins loaded');
|
|
51
51
|
}
|
|
@@ -67,7 +67,9 @@ export class PluginManager {
|
|
|
67
67
|
if (!plugin) {
|
|
68
68
|
throw new Error(`Plugin not found: ${name}`);
|
|
69
69
|
}
|
|
70
|
-
if (plugin.validate &&
|
|
70
|
+
if (plugin.validate &&
|
|
71
|
+
config?.options &&
|
|
72
|
+
!plugin.validate(config.options)) {
|
|
71
73
|
throw new Error(`Invalid options for plugin: ${name}`);
|
|
72
74
|
}
|
|
73
75
|
this.activePlugins.add(name);
|
|
@@ -113,7 +115,7 @@ export class PluginManager {
|
|
|
113
115
|
return Array.from(this.plugins.entries()).map(([name, plugin]) => ({
|
|
114
116
|
name,
|
|
115
117
|
version: plugin.version,
|
|
116
|
-
enabled: this.activePlugins.has(name)
|
|
118
|
+
enabled: this.activePlugins.has(name),
|
|
117
119
|
}));
|
|
118
120
|
}
|
|
119
121
|
/**
|