svger-cli 2.0.8 → 3.1.0

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.
@@ -0,0 +1,273 @@
1
+ /**
2
+ * Official Webpack Plugin for SVGER-CLI
3
+ *
4
+ * Automatically converts SVG files to framework components during webpack builds
5
+ * with full HMR support and optimization.
6
+ *
7
+ * @example
8
+ * ```js
9
+ * // webpack.config.js
10
+ * const { SvgerWebpackPlugin } = require('svger-cli/webpack');
11
+ *
12
+ * module.exports = {
13
+ * plugins: [
14
+ * new SvgerWebpackPlugin({
15
+ * source: './src/icons',
16
+ * output: './src/components/icons',
17
+ * framework: 'react',
18
+ * typescript: true
19
+ * })
20
+ * ]
21
+ * };
22
+ * ```
23
+ */
24
+ import path from 'path';
25
+ import { FileSystem } from '../utils/native.js';
26
+ import { svgProcessor } from '../processors/svg-processor.js';
27
+ import { configService } from '../services/config.js';
28
+ import { logger } from '../core/logger.js';
29
+ const PLUGIN_NAME = 'SvgerWebpackPlugin';
30
+ /**
31
+ * Webpack Plugin for SVGER-CLI
32
+ */
33
+ export class SvgerWebpackPlugin {
34
+ options;
35
+ processedFiles = new Set();
36
+ watchDebounceTimer;
37
+ constructor(options = {}) {
38
+ const config = configService.readConfig();
39
+ this.options = {
40
+ source: options.source || config.source || './src/icons',
41
+ output: options.output || config.output || './src/components/icons',
42
+ framework: options.framework || config.framework || 'react',
43
+ typescript: options.typescript !== undefined
44
+ ? options.typescript
45
+ : config.typescript,
46
+ config: options.config || {},
47
+ include: options.include || ['**/*.svg'],
48
+ exclude: options.exclude || ['node_modules/**'],
49
+ hmr: options.hmr !== undefined ? options.hmr : true,
50
+ emitFile: options.emitFile !== undefined ? options.emitFile : true,
51
+ generateIndex: options.generateIndex !== undefined ? options.generateIndex : true,
52
+ watch: {
53
+ debounce: options.watch?.debounce || 300,
54
+ ignored: options.watch?.ignored || ['node_modules/**'],
55
+ },
56
+ };
57
+ }
58
+ apply(compiler) {
59
+ // Initial build - process all SVG files
60
+ compiler.hooks.beforeCompile.tapPromise(PLUGIN_NAME, async () => {
61
+ logger.info('SVGER Webpack Plugin: Processing SVG files...');
62
+ await this.processAllSVGs();
63
+ });
64
+ // Watch mode - process changed files
65
+ if (compiler.options.watch) {
66
+ compiler.hooks.watchRun.tapPromise(PLUGIN_NAME, async (comp) => {
67
+ const changedFiles = this.getChangedFiles(comp);
68
+ if (changedFiles.size > 0) {
69
+ await this.processChangedFiles(changedFiles);
70
+ }
71
+ });
72
+ }
73
+ // Add HMR support
74
+ if (this.options.hmr && compiler.options.devServer) {
75
+ compiler.hooks.done.tap(PLUGIN_NAME, (stats) => {
76
+ if (stats.hasErrors())
77
+ return;
78
+ const changedFiles = Array.from(this.processedFiles);
79
+ if (changedFiles.length > 0) {
80
+ logger.info(`SVGER: ${changedFiles.length} component(s) updated with HMR`);
81
+ }
82
+ this.processedFiles.clear();
83
+ });
84
+ }
85
+ }
86
+ /**
87
+ * Process all SVG files in the source directory
88
+ */
89
+ async processAllSVGs() {
90
+ const sourceDir = path.resolve(this.options.source);
91
+ const outputDir = path.resolve(this.options.output);
92
+ if (!(await FileSystem.exists(sourceDir))) {
93
+ logger.warn(`Source directory not found: ${sourceDir}`);
94
+ return;
95
+ }
96
+ await FileSystem.ensureDir(outputDir);
97
+ const files = await FileSystem.readDir(sourceDir);
98
+ const svgFiles = files.filter((file) => file.endsWith('.svg'));
99
+ if (svgFiles.length === 0) {
100
+ logger.warn('No SVG files found in source directory');
101
+ return;
102
+ }
103
+ const results = [];
104
+ for (const file of svgFiles) {
105
+ const svgPath = path.join(sourceDir, file);
106
+ const result = await this.processSVGFile(svgPath, outputDir);
107
+ results.push(result);
108
+ if (result.success && result.outputPath) {
109
+ this.processedFiles.add(result.outputPath);
110
+ }
111
+ }
112
+ // Generate index file
113
+ if (this.options.generateIndex) {
114
+ await this.generateIndexFile(outputDir, results.filter(r => r.success));
115
+ }
116
+ const successful = results.filter(r => r.success).length;
117
+ logger.info(`SVGER: Processed ${successful}/${svgFiles.length} SVG files`);
118
+ }
119
+ /**
120
+ * Process changed files in watch mode
121
+ */
122
+ async processChangedFiles(files) {
123
+ const outputDir = path.resolve(this.options.output);
124
+ // Debounce processing
125
+ if (this.watchDebounceTimer) {
126
+ clearTimeout(this.watchDebounceTimer);
127
+ }
128
+ await new Promise(resolve => {
129
+ this.watchDebounceTimer = setTimeout(async () => {
130
+ for (const file of files) {
131
+ if (file.endsWith('.svg')) {
132
+ const result = await this.processSVGFile(file, outputDir);
133
+ if (result.success && result.outputPath) {
134
+ this.processedFiles.add(result.outputPath);
135
+ logger.info(`SVGER: Updated ${result.componentName}`);
136
+ }
137
+ }
138
+ }
139
+ resolve();
140
+ }, this.options.watch.debounce);
141
+ });
142
+ }
143
+ /**
144
+ * Process a single SVG file
145
+ */
146
+ async processSVGFile(svgPath, outputDir) {
147
+ try {
148
+ const mergedConfig = {
149
+ ...configService.readConfig(),
150
+ ...this.options.config,
151
+ framework: this.options.framework,
152
+ typescript: this.options.typescript,
153
+ };
154
+ const result = await svgProcessor.processSVGFile(svgPath, outputDir, {
155
+ framework: mergedConfig.framework,
156
+ typescript: mergedConfig.typescript,
157
+ defaultWidth: mergedConfig.defaultWidth,
158
+ defaultHeight: mergedConfig.defaultHeight,
159
+ defaultFill: mergedConfig.defaultFill,
160
+ styleRules: mergedConfig.styleRules || {},
161
+ });
162
+ return {
163
+ success: result.success,
164
+ outputPath: result.filePath || undefined,
165
+ componentName: result.componentName,
166
+ error: result.error,
167
+ };
168
+ }
169
+ catch (error) {
170
+ logger.error(`Failed to process ${svgPath}:`, error);
171
+ return {
172
+ success: false,
173
+ error: error,
174
+ };
175
+ }
176
+ }
177
+ /**
178
+ * Generate index file with all exports
179
+ */
180
+ async generateIndexFile(outputDir, results) {
181
+ const extension = this.options.typescript ? 'ts' : 'js';
182
+ const exports = results
183
+ .filter(r => r.componentName)
184
+ .map(r => `export { default as ${r.componentName} } from './${r.componentName}';`)
185
+ .join('\n');
186
+ const indexPath = path.join(outputDir, `index.${extension}`);
187
+ await FileSystem.writeFile(indexPath, exports, 'utf-8');
188
+ logger.debug(`Generated index file: ${indexPath}`);
189
+ }
190
+ /**
191
+ * Get changed files from webpack
192
+ */
193
+ getChangedFiles(compiler) {
194
+ const changedFiles = new Set();
195
+ const watchFileSystem = compiler.watchFileSystem;
196
+ if (watchFileSystem && watchFileSystem.watcher) {
197
+ const { mtimes } = watchFileSystem.watcher;
198
+ if (mtimes) {
199
+ for (const file of Object.keys(mtimes)) {
200
+ if (file.endsWith('.svg') && file.includes(this.options.source)) {
201
+ changedFiles.add(file);
202
+ }
203
+ }
204
+ }
205
+ }
206
+ return changedFiles;
207
+ }
208
+ }
209
+ /**
210
+ * Webpack Loader for SVGER-CLI
211
+ *
212
+ * Transforms SVG imports into framework components inline
213
+ *
214
+ * @example
215
+ * ```js
216
+ * // webpack.config.js
217
+ * module.exports = {
218
+ * module: {
219
+ * rules: [
220
+ * {
221
+ * test: /\.svg$/,
222
+ * use: [
223
+ * {
224
+ * loader: 'svger-cli/webpack-loader',
225
+ * options: {
226
+ * framework: 'react',
227
+ * typescript: true
228
+ * }
229
+ * }
230
+ * ]
231
+ * }
232
+ * ]
233
+ * }
234
+ * };
235
+ * ```
236
+ */
237
+ export async function svgerLoader(content) {
238
+ const callback = this.async();
239
+ const options = this.getOptions() || {};
240
+ const config = configService.readConfig();
241
+ const framework = options.framework || config.framework || 'react';
242
+ const typescript = options.typescript !== undefined ? options.typescript : config.typescript;
243
+ const componentName = svgProcessor.generateComponentName(this.resourcePath, 'pascal');
244
+ try {
245
+ const component = await svgProcessor.generateComponent(componentName, content, {
246
+ framework,
247
+ typescript,
248
+ defaultWidth: config.defaultWidth,
249
+ defaultHeight: config.defaultHeight,
250
+ defaultFill: config.defaultFill,
251
+ styleRules: config.styleRules || {},
252
+ });
253
+ // Enable HMR for the loader
254
+ if (this.hot) {
255
+ const hmrCode = `
256
+ if (module.hot) {
257
+ module.hot.accept();
258
+ }
259
+ `;
260
+ callback(null, component + '\n' + hmrCode);
261
+ }
262
+ else {
263
+ callback(null, component);
264
+ }
265
+ }
266
+ catch (error) {
267
+ callback(error);
268
+ }
269
+ // Return empty string to satisfy TypeScript (callback handles the actual return)
270
+ return '';
271
+ }
272
+ // Default export for plugin
273
+ export default SvgerWebpackPlugin;
@@ -196,3 +196,4 @@ export interface ProcessingJob {
196
196
  endTime?: number;
197
197
  error?: Error;
198
198
  }
199
+ export * from './integrations.js';
@@ -1,4 +1,5 @@
1
1
  /**
2
2
  * Type definitions for svger-cli
3
3
  */
4
- export {};
4
+ // Re-export integration types
5
+ export * from './integrations.js';
@@ -0,0 +1,255 @@
1
+ /**
2
+ * Type definitions for build tool integrations
3
+ */
4
+ import type { FrameworkType, SVGConfig } from './index.js';
5
+ /**
6
+ * Base options for all integrations
7
+ */
8
+ export interface BaseIntegrationOptions {
9
+ /**
10
+ * Source directory containing SVG files
11
+ */
12
+ source?: string;
13
+ /**
14
+ * Output directory for generated components
15
+ */
16
+ output?: string;
17
+ /**
18
+ * Framework to generate components for
19
+ */
20
+ framework?: FrameworkType;
21
+ /**
22
+ * Whether to use TypeScript
23
+ */
24
+ typescript?: boolean;
25
+ /**
26
+ * Custom configuration
27
+ */
28
+ config?: Partial<SVGConfig>;
29
+ /**
30
+ * Include pattern (glob)
31
+ */
32
+ include?: string | string[];
33
+ /**
34
+ * Exclude pattern (glob)
35
+ */
36
+ exclude?: string | string[];
37
+ }
38
+ export interface WebpackPluginOptions extends BaseIntegrationOptions {
39
+ /**
40
+ * Enable Hot Module Replacement
41
+ * @default true
42
+ */
43
+ hmr?: boolean;
44
+ /**
45
+ * Emit files to output directory
46
+ * @default true
47
+ */
48
+ emitFile?: boolean;
49
+ /**
50
+ * Generate index file with exports
51
+ * @default true
52
+ */
53
+ generateIndex?: boolean;
54
+ /**
55
+ * Watch mode options
56
+ */
57
+ watch?: {
58
+ /**
59
+ * Debounce delay in milliseconds
60
+ * @default 300
61
+ */
62
+ debounce?: number;
63
+ /**
64
+ * Paths to ignore
65
+ */
66
+ ignored?: string | string[];
67
+ };
68
+ }
69
+ /**
70
+ * Webpack loader options
71
+ */
72
+ export interface WebpackLoaderOptions extends BaseIntegrationOptions {
73
+ /**
74
+ * Export format
75
+ * @default 'default'
76
+ */
77
+ exportType?: 'default' | 'named';
78
+ /**
79
+ * SVGO optimization options
80
+ */
81
+ svgo?: boolean | Record<string, any>;
82
+ }
83
+ export interface VitePluginOptions extends BaseIntegrationOptions {
84
+ /**
85
+ * Enable Hot Module Replacement
86
+ * @default true
87
+ */
88
+ hmr?: boolean;
89
+ /**
90
+ * Use virtual modules for components
91
+ * @default false
92
+ */
93
+ virtual?: boolean;
94
+ /**
95
+ * Default export or named export
96
+ * @default 'default'
97
+ */
98
+ exportType?: 'default' | 'named';
99
+ /**
100
+ * SVGO optimization options
101
+ */
102
+ svgo?: boolean | Record<string, any>;
103
+ /**
104
+ * Generate index file with exports
105
+ * @default true
106
+ */
107
+ generateIndex?: boolean;
108
+ }
109
+ export interface RollupPluginOptions extends BaseIntegrationOptions {
110
+ /**
111
+ * Default export or named export
112
+ * @default 'default'
113
+ */
114
+ exportType?: 'default' | 'named';
115
+ /**
116
+ * SVGO optimization options
117
+ */
118
+ svgo?: boolean | Record<string, any>;
119
+ /**
120
+ * Generate index file with exports
121
+ * @default true
122
+ */
123
+ generateIndex?: boolean;
124
+ /**
125
+ * Generate source maps
126
+ * @default false
127
+ */
128
+ sourcemap?: boolean;
129
+ }
130
+ export interface NextJsPluginOptions extends BaseIntegrationOptions {
131
+ /**
132
+ * Enable Hot Module Replacement
133
+ * @default true
134
+ */
135
+ hmr?: boolean;
136
+ /**
137
+ * Server-side rendering support
138
+ * @default true
139
+ */
140
+ ssr?: boolean;
141
+ /**
142
+ * Generate index file with exports
143
+ * @default true
144
+ */
145
+ generateIndex?: boolean;
146
+ /**
147
+ * Webpack configuration modifier
148
+ */
149
+ webpack?: (config: any, options: any) => any;
150
+ }
151
+ export interface BabelPluginOptions extends BaseIntegrationOptions {
152
+ /**
153
+ * Transform SVG imports to component imports
154
+ * @default true
155
+ */
156
+ transformImports?: boolean;
157
+ /**
158
+ * Process all SVGs when plugin is initialized
159
+ * @default true
160
+ */
161
+ processOnInit?: boolean;
162
+ /**
163
+ * Generate index file with all components
164
+ * @default true
165
+ */
166
+ generateIndex?: boolean;
167
+ }
168
+ export interface JestPresetOptions {
169
+ /**
170
+ * Framework to use for component generation in tests
171
+ * @default 'react'
172
+ */
173
+ framework?: FrameworkType;
174
+ /**
175
+ * Use TypeScript
176
+ * @default true
177
+ */
178
+ typescript?: boolean;
179
+ /**
180
+ * Transform SVG files to components
181
+ * @default true
182
+ */
183
+ transform?: boolean;
184
+ /**
185
+ * Mock SVG files as simple React components
186
+ * @default false
187
+ */
188
+ mock?: boolean;
189
+ }
190
+ export interface ProcessingResult {
191
+ /**
192
+ * Whether processing was successful
193
+ */
194
+ success: boolean;
195
+ /**
196
+ * Path to the generated file
197
+ */
198
+ outputPath?: string;
199
+ /**
200
+ * Component name
201
+ */
202
+ componentName?: string;
203
+ /**
204
+ * Generated code
205
+ */
206
+ code?: string;
207
+ /**
208
+ * Source map
209
+ */
210
+ map?: string | null;
211
+ /**
212
+ * Error if processing failed
213
+ */
214
+ error?: Error;
215
+ }
216
+ export interface BatchProcessingResult {
217
+ /**
218
+ * Total files processed
219
+ */
220
+ total: number;
221
+ /**
222
+ * Successful conversions
223
+ */
224
+ successful: number;
225
+ /**
226
+ * Failed conversions
227
+ */
228
+ failed: number;
229
+ /**
230
+ * Individual results
231
+ */
232
+ results: ProcessingResult[];
233
+ }
234
+ export interface IntegrationMetadata {
235
+ /**
236
+ * Name of the build tool
237
+ */
238
+ name: string;
239
+ /**
240
+ * Version of the integration
241
+ */
242
+ version: string;
243
+ /**
244
+ * Supported file extensions
245
+ */
246
+ extensions: string[];
247
+ /**
248
+ * Whether HMR is supported
249
+ */
250
+ supportsHMR: boolean;
251
+ /**
252
+ * Whether source maps are supported
253
+ */
254
+ supportsSourceMaps: boolean;
255
+ }
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Type definitions for build tool integrations
3
+ */
4
+ export {};