svger-cli 2.0.1 → 2.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.
Files changed (91) hide show
  1. package/.svgerconfig.example.json +38 -0
  2. package/CHANGELOG.md +64 -0
  3. package/DEVELOPMENT.md +353 -0
  4. package/README.md +24 -5
  5. package/SECURITY.md +69 -0
  6. package/dist/builder.js +16 -16
  7. package/dist/clean.js +2 -2
  8. package/dist/cli.js +38 -38
  9. package/dist/config.js +11 -11
  10. package/dist/core/error-handler.d.ts +63 -0
  11. package/dist/core/error-handler.js +227 -0
  12. package/dist/core/framework-templates.d.ts +17 -0
  13. package/{src/core/framework-templates.ts → dist/core/framework-templates.js} +104 -139
  14. package/dist/core/logger.d.ts +22 -0
  15. package/dist/core/logger.js +85 -0
  16. package/dist/core/performance-engine.d.ts +67 -0
  17. package/dist/core/performance-engine.js +252 -0
  18. package/dist/core/plugin-manager.d.ts +56 -0
  19. package/dist/core/plugin-manager.js +191 -0
  20. package/dist/core/style-compiler.d.ts +88 -0
  21. package/dist/core/style-compiler.js +468 -0
  22. package/dist/core/template-manager.d.ts +64 -0
  23. package/{src/core/template-manager.ts → dist/core/template-manager.js} +172 -255
  24. package/dist/index.d.ts +153 -0
  25. package/{src/index.ts → dist/index.js} +32 -110
  26. package/dist/lock.js +7 -7
  27. package/dist/processors/svg-processor.d.ts +73 -0
  28. package/dist/processors/svg-processor.js +261 -0
  29. package/dist/services/config.d.ts +55 -0
  30. package/dist/services/config.js +211 -0
  31. package/dist/services/file-watcher.d.ts +54 -0
  32. package/dist/services/file-watcher.js +180 -0
  33. package/dist/services/svg-service.d.ts +81 -0
  34. package/dist/services/svg-service.js +395 -0
  35. package/dist/templates/ComponentTemplate.js +25 -25
  36. package/dist/types/index.d.ts +146 -0
  37. package/dist/types/index.js +4 -0
  38. package/dist/utils/native.d.ts +104 -0
  39. package/dist/utils/native.js +340 -0
  40. package/dist/watch.d.ts +1 -1
  41. package/dist/watch.js +14 -14
  42. package/package.json +154 -14
  43. package/.svgconfig.json +0 -3
  44. package/CODE_OF_CONDUCT.md +0 -79
  45. package/CONTRIBUTING.md +0 -146
  46. package/TESTING.md +0 -143
  47. package/cli-framework.test.js +0 -16
  48. package/cli-test-angular/Arrowbenddownleft.component.ts +0 -27
  49. package/cli-test-angular/Vite.component.ts +0 -27
  50. package/cli-test-angular/index.ts +0 -25
  51. package/cli-test-output/Arrowbenddownleft.vue +0 -33
  52. package/cli-test-output/Vite.vue +0 -33
  53. package/cli-test-output/index.ts +0 -25
  54. package/cli-test-react/Arrowbenddownleft.tsx +0 -39
  55. package/cli-test-react/Vite.tsx +0 -39
  56. package/cli-test-react/index.ts +0 -25
  57. package/cli-test-svelte/Arrowbenddownleft.svelte +0 -22
  58. package/cli-test-svelte/Vite.svelte +0 -22
  59. package/cli-test-svelte/index.ts +0 -25
  60. package/frameworks.test.js +0 -170
  61. package/my-svgs/ArrowBendDownLeft.svg +0 -6
  62. package/my-svgs/vite.svg +0 -1
  63. package/src/builder.ts +0 -104
  64. package/src/clean.ts +0 -21
  65. package/src/cli.ts +0 -221
  66. package/src/config.ts +0 -81
  67. package/src/core/error-handler.ts +0 -303
  68. package/src/core/logger.ts +0 -104
  69. package/src/core/performance-engine.ts +0 -327
  70. package/src/core/plugin-manager.ts +0 -228
  71. package/src/core/style-compiler.ts +0 -605
  72. package/src/lock.ts +0 -74
  73. package/src/processors/svg-processor.ts +0 -288
  74. package/src/services/config.ts +0 -241
  75. package/src/services/file-watcher.ts +0 -218
  76. package/src/services/svg-service.ts +0 -468
  77. package/src/templates/ComponentTemplate.ts +0 -57
  78. package/src/types/index.ts +0 -169
  79. package/src/utils/native.ts +0 -352
  80. package/src/watch.ts +0 -88
  81. package/test-output-mulit/TestIcon-angular-module.component.ts +0 -26
  82. package/test-output-mulit/TestIcon-angular-standalone.component.ts +0 -27
  83. package/test-output-mulit/TestIcon-lit.ts +0 -35
  84. package/test-output-mulit/TestIcon-preact.tsx +0 -38
  85. package/test-output-mulit/TestIcon-react.tsx +0 -35
  86. package/test-output-mulit/TestIcon-solid.tsx +0 -27
  87. package/test-output-mulit/TestIcon-svelte.svelte +0 -22
  88. package/test-output-mulit/TestIcon-vanilla.ts +0 -37
  89. package/test-output-mulit/TestIcon-vue-composition.vue +0 -33
  90. package/test-output-mulit/TestIcon-vue-options.vue +0 -31
  91. package/tsconfig.json +0 -18
@@ -0,0 +1,55 @@
1
+ import { SVGConfig } from '../types/index.js';
2
+ /**
3
+ * Professional configuration management service
4
+ */
5
+ export declare class ConfigService {
6
+ private static instance;
7
+ private static readonly CONFIG_FILE;
8
+ private cachedConfig;
9
+ private constructor();
10
+ static getInstance(): ConfigService;
11
+ /**
12
+ * Get the default configuration
13
+ */
14
+ getDefaultConfig(): SVGConfig;
15
+ /**
16
+ * Get configuration file path
17
+ */
18
+ private getConfigPath;
19
+ /**
20
+ * Read configuration from file with caching
21
+ */
22
+ readConfig(): SVGConfig;
23
+ /**
24
+ * Write configuration to file
25
+ */
26
+ writeConfig(config: SVGConfig): void;
27
+ /**
28
+ * Initialize configuration with defaults
29
+ */
30
+ initConfig(): Promise<void>;
31
+ /**
32
+ * Set a specific configuration value
33
+ */
34
+ setConfig(key: string, value: any): void;
35
+ /**
36
+ * Get a specific configuration value
37
+ */
38
+ getConfig(key?: string): any;
39
+ /**
40
+ * Validate configuration
41
+ */
42
+ validateConfig(config?: SVGConfig): {
43
+ valid: boolean;
44
+ errors: string[];
45
+ };
46
+ /**
47
+ * Clear cached configuration (useful for testing)
48
+ */
49
+ clearCache(): void;
50
+ /**
51
+ * Display current configuration
52
+ */
53
+ showConfig(): void;
54
+ }
55
+ export declare const configService: ConfigService;
@@ -0,0 +1,211 @@
1
+ import path from 'path';
2
+ import { FileSystem } from '../utils/native.js';
3
+ import { logger } from '../core/logger.js';
4
+ /**
5
+ * Professional configuration management service
6
+ */
7
+ export class ConfigService {
8
+ static instance;
9
+ static CONFIG_FILE = '.svgconfig.json';
10
+ cachedConfig = null;
11
+ constructor() { }
12
+ static getInstance() {
13
+ if (!ConfigService.instance) {
14
+ ConfigService.instance = new ConfigService();
15
+ }
16
+ return ConfigService.instance;
17
+ }
18
+ /**
19
+ * Get the default configuration
20
+ */
21
+ getDefaultConfig() {
22
+ return {
23
+ source: './src/assets/svg',
24
+ output: './src/components/icons',
25
+ framework: 'react',
26
+ typescript: true,
27
+ watch: false,
28
+ defaultWidth: 24,
29
+ defaultHeight: 24,
30
+ defaultFill: 'currentColor',
31
+ exclude: [],
32
+ styleRules: {
33
+ fill: 'inherit',
34
+ stroke: 'none',
35
+ },
36
+ plugins: [],
37
+ template: {
38
+ type: 'default',
39
+ },
40
+ frameworkOptions: {
41
+ forwardRef: true,
42
+ memo: false,
43
+ scriptSetup: true,
44
+ standalone: true,
45
+ },
46
+ errorHandling: {
47
+ skipOnError: false,
48
+ logLevel: 'info',
49
+ maxRetries: 3,
50
+ },
51
+ performance: {
52
+ batchSize: 10,
53
+ parallel: true,
54
+ timeout: 30000,
55
+ enableCache: true,
56
+ },
57
+ };
58
+ }
59
+ /**
60
+ * Get configuration file path
61
+ */
62
+ getConfigPath() {
63
+ return path.resolve(ConfigService.CONFIG_FILE);
64
+ }
65
+ /**
66
+ * Read configuration from file with caching
67
+ */
68
+ readConfig() {
69
+ if (this.cachedConfig) {
70
+ return { ...this.cachedConfig };
71
+ }
72
+ try {
73
+ const configData = FileSystem.readJSONSync(this.getConfigPath());
74
+ if (Object.keys(configData).length === 0) {
75
+ logger.debug('No configuration found, using defaults');
76
+ this.cachedConfig = this.getDefaultConfig();
77
+ return this.cachedConfig;
78
+ }
79
+ // Merge with defaults to ensure all required properties exist
80
+ this.cachedConfig = {
81
+ ...this.getDefaultConfig(),
82
+ ...configData,
83
+ };
84
+ logger.debug('Configuration loaded successfully');
85
+ return this.cachedConfig;
86
+ }
87
+ catch (error) {
88
+ logger.warn('Failed to read configuration, using defaults:', error);
89
+ this.cachedConfig = this.getDefaultConfig();
90
+ return this.cachedConfig;
91
+ }
92
+ }
93
+ /**
94
+ * Write configuration to file
95
+ */
96
+ writeConfig(config) {
97
+ try {
98
+ FileSystem.writeJSONSync(this.getConfigPath(), config, { spaces: 2 });
99
+ this.cachedConfig = config; // Update cache
100
+ logger.success('Configuration saved successfully');
101
+ }
102
+ catch (error) {
103
+ logger.error('Failed to write configuration:', error);
104
+ throw error;
105
+ }
106
+ }
107
+ /**
108
+ * Initialize configuration with defaults
109
+ */
110
+ async initConfig() {
111
+ const configPath = this.getConfigPath();
112
+ if (await FileSystem.exists(configPath)) {
113
+ logger.warn('Config file already exists:', configPath);
114
+ return;
115
+ }
116
+ const defaultConfig = this.getDefaultConfig();
117
+ this.writeConfig(defaultConfig);
118
+ logger.success('Configuration initialized with defaults');
119
+ }
120
+ /**
121
+ * Set a specific configuration value
122
+ */
123
+ setConfig(key, value) {
124
+ const config = this.readConfig();
125
+ // Support nested key paths like 'styleRules.fill'
126
+ const keys = key.split('.');
127
+ let current = config;
128
+ for (let i = 0; i < keys.length - 1; i++) {
129
+ const k = keys[i];
130
+ if (!(k in current)) {
131
+ current[k] = {};
132
+ }
133
+ current = current[k];
134
+ }
135
+ const finalKey = keys[keys.length - 1];
136
+ current[finalKey] = value;
137
+ this.writeConfig(config);
138
+ logger.success(`Configuration updated: ${key} = ${value}`);
139
+ }
140
+ /**
141
+ * Get a specific configuration value
142
+ */
143
+ getConfig(key) {
144
+ const config = this.readConfig();
145
+ if (!key) {
146
+ return config;
147
+ }
148
+ // Support nested key paths
149
+ const keys = key.split('.');
150
+ let current = config;
151
+ for (const k of keys) {
152
+ if (!(k in current)) {
153
+ return undefined;
154
+ }
155
+ current = current[k];
156
+ }
157
+ return current;
158
+ }
159
+ /**
160
+ * Validate configuration
161
+ */
162
+ validateConfig(config) {
163
+ const configToValidate = config || this.readConfig();
164
+ const errors = [];
165
+ // Required string fields
166
+ const requiredStringFields = ['source', 'output', 'defaultFill'];
167
+ for (const field of requiredStringFields) {
168
+ if (!configToValidate[field] ||
169
+ typeof configToValidate[field] !== 'string') {
170
+ errors.push(`${field} must be a non-empty string`);
171
+ }
172
+ }
173
+ // Required numeric fields
174
+ const requiredNumericFields = ['defaultWidth', 'defaultHeight'];
175
+ for (const field of requiredNumericFields) {
176
+ const value = configToValidate[field];
177
+ if (typeof value !== 'number' || value <= 0) {
178
+ errors.push(`${field} must be a positive number`);
179
+ }
180
+ }
181
+ // Validate exclude array
182
+ if (configToValidate.exclude && !Array.isArray(configToValidate.exclude)) {
183
+ errors.push('exclude must be an array');
184
+ }
185
+ // Validate styleRules object
186
+ if (configToValidate.styleRules &&
187
+ typeof configToValidate.styleRules !== 'object') {
188
+ errors.push('styleRules must be an object');
189
+ }
190
+ return {
191
+ valid: errors.length === 0,
192
+ errors,
193
+ };
194
+ }
195
+ /**
196
+ * Clear cached configuration (useful for testing)
197
+ */
198
+ clearCache() {
199
+ this.cachedConfig = null;
200
+ }
201
+ /**
202
+ * Display current configuration
203
+ */
204
+ showConfig() {
205
+ const config = this.readConfig();
206
+ console.log('📄 Current Configuration:');
207
+ console.log(JSON.stringify(config, null, 2));
208
+ }
209
+ }
210
+ // Export singleton instance
211
+ export const configService = ConfigService.getInstance();
@@ -0,0 +1,54 @@
1
+ import { FileWatchEvent, WatchOptions } from '../types/index.js';
2
+ /**
3
+ * Professional file watching service with debouncing and event filtering
4
+ */
5
+ export declare class FileWatcherService {
6
+ private static instance;
7
+ private watchers;
8
+ private eventHandlers;
9
+ private debounceTimers;
10
+ private readonly debounceDelay;
11
+ private constructor();
12
+ static getInstance(): FileWatcherService;
13
+ /**
14
+ * Start watching a directory for SVG file changes
15
+ */
16
+ watchDirectory(watchPath: string, options?: Partial<WatchOptions>): Promise<string>;
17
+ /**
18
+ * Handle file system events with debouncing
19
+ */
20
+ private handleFileEvent;
21
+ /**
22
+ * Process debounced file events
23
+ */
24
+ private processFileEvent;
25
+ /**
26
+ * Register an event handler for a specific watcher
27
+ */
28
+ onFileEvent(watchId: string, handler: (event: FileWatchEvent) => void | Promise<void>): void;
29
+ /**
30
+ * Emit event to all registered handlers
31
+ */
32
+ private emitEvent;
33
+ /**
34
+ * Stop watching a specific directory
35
+ */
36
+ stopWatching(watchId: string): void;
37
+ /**
38
+ * Stop all watchers
39
+ */
40
+ stopAllWatchers(): void;
41
+ /**
42
+ * Get active watch statistics
43
+ */
44
+ getWatchStats(): {
45
+ activeWatchers: number;
46
+ pendingEvents: number;
47
+ totalHandlers: number;
48
+ };
49
+ /**
50
+ * Cleanup on shutdown
51
+ */
52
+ shutdown(): void;
53
+ }
54
+ export declare const fileWatcher: FileWatcherService;
@@ -0,0 +1,180 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import { FileSystem } from '../utils/native.js';
4
+ import { logger } from '../core/logger.js';
5
+ /**
6
+ * Professional file watching service with debouncing and event filtering
7
+ */
8
+ export class FileWatcherService {
9
+ static instance;
10
+ watchers = new Map();
11
+ eventHandlers = new Map();
12
+ debounceTimers = new Map();
13
+ debounceDelay = 300; // milliseconds
14
+ constructor() { }
15
+ static getInstance() {
16
+ if (!FileWatcherService.instance) {
17
+ FileWatcherService.instance = new FileWatcherService();
18
+ }
19
+ return FileWatcherService.instance;
20
+ }
21
+ /**
22
+ * Start watching a directory for SVG file changes
23
+ */
24
+ async watchDirectory(watchPath, options = {}) {
25
+ const resolvedPath = path.resolve(watchPath);
26
+ if (!(await FileSystem.exists(resolvedPath))) {
27
+ throw new Error(`Watch path does not exist: ${resolvedPath}`);
28
+ }
29
+ const watchId = `watch-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
30
+ try {
31
+ const watcher = fs.watch(resolvedPath, {
32
+ persistent: true,
33
+ recursive: false,
34
+ }, (eventType, filename) => {
35
+ if (filename) {
36
+ this.handleFileEvent(watchId, eventType, path.join(resolvedPath, filename));
37
+ }
38
+ });
39
+ this.watchers.set(watchId, watcher);
40
+ this.eventHandlers.set(watchId, []);
41
+ logger.info(`Started watching directory: ${resolvedPath}`);
42
+ return watchId;
43
+ }
44
+ catch (error) {
45
+ logger.error(`Failed to start watching ${resolvedPath}:`, error);
46
+ throw error;
47
+ }
48
+ }
49
+ /**
50
+ * Handle file system events with debouncing
51
+ */
52
+ handleFileEvent(watchId, eventType, filePath) {
53
+ // Only process SVG files
54
+ if (!filePath.endsWith('.svg')) {
55
+ return;
56
+ }
57
+ const debounceKey = `${watchId}-${filePath}`;
58
+ // Clear existing timer
59
+ if (this.debounceTimers.has(debounceKey)) {
60
+ clearTimeout(this.debounceTimers.get(debounceKey));
61
+ }
62
+ // Set new timer
63
+ const timer = setTimeout(async () => {
64
+ this.debounceTimers.delete(debounceKey);
65
+ await this.processFileEvent(watchId, eventType, filePath);
66
+ }, this.debounceDelay);
67
+ this.debounceTimers.set(debounceKey, timer);
68
+ }
69
+ /**
70
+ * Process debounced file events
71
+ */
72
+ async processFileEvent(watchId, eventType, filePath) {
73
+ try {
74
+ const exists = await FileSystem.exists(filePath);
75
+ let actualEventType;
76
+ // Determine actual event type
77
+ if (eventType === 'rename') {
78
+ actualEventType = exists ? 'add' : 'unlink';
79
+ }
80
+ else {
81
+ actualEventType = 'change';
82
+ }
83
+ const event = {
84
+ type: actualEventType,
85
+ filePath,
86
+ timestamp: Date.now(),
87
+ };
88
+ logger.debug(`File event: ${actualEventType} - ${path.basename(filePath)}`);
89
+ // Emit event to registered handlers
90
+ this.emitEvent(watchId, event);
91
+ }
92
+ catch (error) {
93
+ logger.error(`Error processing file event for ${filePath}:`, error);
94
+ }
95
+ }
96
+ /**
97
+ * Register an event handler for a specific watcher
98
+ */
99
+ onFileEvent(watchId, handler) {
100
+ const handlers = this.eventHandlers.get(watchId) || [];
101
+ handlers.push(handler);
102
+ this.eventHandlers.set(watchId, handlers);
103
+ }
104
+ /**
105
+ * Emit event to all registered handlers
106
+ */
107
+ emitEvent(watchId, event) {
108
+ const handlers = this.eventHandlers.get(watchId) || [];
109
+ for (const handler of handlers) {
110
+ try {
111
+ const result = handler(event);
112
+ // Handle async handlers
113
+ if (result && typeof result.then === 'function') {
114
+ result.catch((error) => {
115
+ logger.error(`Error in file event handler:`, error);
116
+ });
117
+ }
118
+ }
119
+ catch (error) {
120
+ logger.error(`Error in file event handler:`, error);
121
+ }
122
+ }
123
+ }
124
+ /**
125
+ * Stop watching a specific directory
126
+ */
127
+ stopWatching(watchId) {
128
+ const watcher = this.watchers.get(watchId);
129
+ if (watcher) {
130
+ watcher.close();
131
+ this.watchers.delete(watchId);
132
+ this.eventHandlers.delete(watchId);
133
+ // Clear any pending debounce timers for this watcher
134
+ for (const [key, timer] of this.debounceTimers.entries()) {
135
+ if (key.startsWith(`${watchId}-`)) {
136
+ clearTimeout(timer);
137
+ this.debounceTimers.delete(key);
138
+ }
139
+ }
140
+ logger.info(`Stopped watching: ${watchId}`);
141
+ }
142
+ }
143
+ /**
144
+ * Stop all watchers
145
+ */
146
+ stopAllWatchers() {
147
+ for (const watchId of this.watchers.keys()) {
148
+ this.stopWatching(watchId);
149
+ }
150
+ logger.info('Stopped all file watchers');
151
+ }
152
+ /**
153
+ * Get active watch statistics
154
+ */
155
+ getWatchStats() {
156
+ let totalHandlers = 0;
157
+ for (const handlers of this.eventHandlers.values()) {
158
+ totalHandlers += handlers.length;
159
+ }
160
+ return {
161
+ activeWatchers: this.watchers.size,
162
+ pendingEvents: this.debounceTimers.size,
163
+ totalHandlers,
164
+ };
165
+ }
166
+ /**
167
+ * Cleanup on shutdown
168
+ */
169
+ shutdown() {
170
+ this.stopAllWatchers();
171
+ // Clear all debounce timers
172
+ for (const timer of this.debounceTimers.values()) {
173
+ clearTimeout(timer);
174
+ }
175
+ this.debounceTimers.clear();
176
+ logger.info('File watcher service shutdown complete');
177
+ }
178
+ }
179
+ // Export singleton instance
180
+ export const fileWatcher = FileWatcherService.getInstance();
@@ -0,0 +1,81 @@
1
+ import { BuildOptions, GenerateOptions, WatchOptions } from '../types/index.js';
2
+ /**
3
+ * Main SVG service that orchestrates all SVG processing operations
4
+ */
5
+ export declare class SVGService {
6
+ private static instance;
7
+ private activeWatchers;
8
+ lockService: LockService;
9
+ private constructor();
10
+ static getInstance(): SVGService;
11
+ /**
12
+ * Build all SVG files from source to output directory
13
+ */
14
+ buildAll(options: BuildOptions): Promise<void>;
15
+ /**
16
+ * Generate a React component from a single SVG file
17
+ */
18
+ generateSingle(options: GenerateOptions): Promise<void>;
19
+ /**
20
+ * Start watching SVG files for changes
21
+ */
22
+ startWatching(options: WatchOptions): Promise<string>;
23
+ /**
24
+ * Handle file watch events
25
+ */
26
+ private handleWatchEvent;
27
+ /**
28
+ * Process a watched file
29
+ */
30
+ private processWatchedFile;
31
+ /**
32
+ * Handle file removal in watch mode
33
+ */
34
+ private handleFileRemoval;
35
+ /**
36
+ * Stop watching files
37
+ */
38
+ stopWatching(watchId?: string): void;
39
+ /**
40
+ * Clean output directory
41
+ */
42
+ clean(outDir: string): Promise<void>;
43
+ /**
44
+ * Generate index.ts file with all component exports
45
+ */
46
+ private generateIndexFile;
47
+ /**
48
+ * Generate the content for index.ts file
49
+ */
50
+ private generateIndexContent;
51
+ /**
52
+ * Get service statistics
53
+ */
54
+ getStats(): {
55
+ activeWatchers: number;
56
+ processingQueue: any;
57
+ watcherStats: any;
58
+ };
59
+ /**
60
+ * Shutdown service
61
+ */
62
+ shutdown(): void;
63
+ }
64
+ /**
65
+ * Simple file locking service
66
+ */
67
+ export declare class LockService {
68
+ private static instance;
69
+ private static readonly LOCK_FILE;
70
+ private cachedLocks;
71
+ private constructor();
72
+ static getInstance(): LockService;
73
+ private getLockFilePath;
74
+ private readLockFile;
75
+ private writeLockFile;
76
+ lockFiles(files: string[]): void;
77
+ unlockFiles(files: string[]): void;
78
+ isLocked(file: string): boolean;
79
+ clearCache(): void;
80
+ }
81
+ export declare const svgService: SVGService;