baseguard 1.0.3 → 1.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.
Files changed (167) hide show
  1. package/.baseguardrc.example.json +63 -63
  2. package/.eslintrc.json +24 -24
  3. package/.prettierrc +7 -7
  4. package/CHANGELOG.md +195 -195
  5. package/DEPLOYMENT.md +624 -624
  6. package/DEPLOYMENT_CHECKLIST.md +239 -239
  7. package/DEPLOYMENT_SUMMARY_v1.0.2.md +202 -202
  8. package/QUICK_START.md +134 -134
  9. package/README.md +488 -488
  10. package/RELEASE_NOTES_v1.0.2.md +434 -434
  11. package/bin/base.js +627 -627
  12. package/dist/ai/fix-manager.d.ts.map +1 -1
  13. package/dist/ai/fix-manager.js +1 -1
  14. package/dist/ai/fix-manager.js.map +1 -1
  15. package/dist/ai/gemini-analyzer.d.ts.map +1 -1
  16. package/dist/ai/gemini-analyzer.js +29 -35
  17. package/dist/ai/gemini-analyzer.js.map +1 -1
  18. package/dist/ai/gemini-code-fixer.d.ts.map +1 -1
  19. package/dist/ai/gemini-code-fixer.js +58 -58
  20. package/dist/ai/gemini-code-fixer.js.map +1 -1
  21. package/dist/ai/jules-implementer.d.ts +3 -0
  22. package/dist/ai/jules-implementer.d.ts.map +1 -1
  23. package/dist/ai/jules-implementer.js +63 -32
  24. package/dist/ai/jules-implementer.js.map +1 -1
  25. package/dist/ai/unified-code-fixer.js.map +1 -1
  26. package/dist/commands/check.d.ts.map +1 -1
  27. package/dist/commands/check.js +1 -1
  28. package/dist/commands/check.js.map +1 -1
  29. package/dist/commands/config.js +2 -1
  30. package/dist/commands/config.js.map +1 -1
  31. package/dist/commands/fix.d.ts.map +1 -1
  32. package/dist/commands/fix.js +44 -15
  33. package/dist/commands/fix.js.map +1 -1
  34. package/dist/core/api-key-manager.js +2 -2
  35. package/dist/core/api-key-manager.js.map +1 -1
  36. package/dist/core/baseguard.d.ts +1 -0
  37. package/dist/core/baseguard.d.ts.map +1 -1
  38. package/dist/core/baseguard.js +13 -10
  39. package/dist/core/baseguard.js.map +1 -1
  40. package/dist/core/baseline-checker.d.ts.map +1 -1
  41. package/dist/core/baseline-checker.js +2 -1
  42. package/dist/core/baseline-checker.js.map +1 -1
  43. package/dist/core/configuration-recovery.d.ts.map +1 -1
  44. package/dist/core/configuration-recovery.js +1 -1
  45. package/dist/core/configuration-recovery.js.map +1 -1
  46. package/dist/core/debug-logger.d.ts.map +1 -1
  47. package/dist/core/debug-logger.js +1 -1
  48. package/dist/core/debug-logger.js.map +1 -1
  49. package/dist/core/error-handler.d.ts.map +1 -1
  50. package/dist/core/error-handler.js +2 -1
  51. package/dist/core/error-handler.js.map +1 -1
  52. package/dist/core/gitignore-manager.js +5 -5
  53. package/dist/core/graceful-degradation-manager.d.ts.map +1 -1
  54. package/dist/core/graceful-degradation-manager.js +16 -16
  55. package/dist/core/graceful-degradation-manager.js.map +1 -1
  56. package/dist/core/lazy-loader.d.ts.map +1 -1
  57. package/dist/core/lazy-loader.js +9 -2
  58. package/dist/core/lazy-loader.js.map +1 -1
  59. package/dist/core/memory-manager.d.ts +0 -3
  60. package/dist/core/memory-manager.d.ts.map +1 -1
  61. package/dist/core/memory-manager.js.map +1 -1
  62. package/dist/core/parser-worker.d.ts +2 -0
  63. package/dist/core/parser-worker.d.ts.map +1 -0
  64. package/dist/core/parser-worker.js +19 -0
  65. package/dist/core/parser-worker.js.map +1 -0
  66. package/dist/core/startup-optimizer.d.ts.map +1 -1
  67. package/dist/core/startup-optimizer.js +4 -8
  68. package/dist/core/startup-optimizer.js.map +1 -1
  69. package/dist/core/system-error-handler.d.ts.map +1 -1
  70. package/dist/core/system-error-handler.js.map +1 -1
  71. package/dist/git/automation-engine.d.ts.map +1 -1
  72. package/dist/git/automation-engine.js +5 -4
  73. package/dist/git/automation-engine.js.map +1 -1
  74. package/dist/git/github-manager.d.ts.map +1 -1
  75. package/dist/git/github-manager.js.map +1 -1
  76. package/dist/git/hook-manager.js +5 -5
  77. package/dist/git/hook-manager.js.map +1 -1
  78. package/dist/parsers/parser-manager.d.ts.map +1 -1
  79. package/dist/parsers/parser-manager.js +1 -1
  80. package/dist/parsers/parser-manager.js.map +1 -1
  81. package/dist/parsers/svelte-parser.js +1 -1
  82. package/dist/parsers/svelte-parser.js.map +1 -1
  83. package/dist/parsers/vanilla-parser.d.ts.map +1 -1
  84. package/dist/parsers/vanilla-parser.js.map +1 -1
  85. package/dist/parsers/vue-parser.d.ts.map +1 -1
  86. package/dist/parsers/vue-parser.js.map +1 -1
  87. package/dist/ui/components.d.ts +1 -1
  88. package/dist/ui/components.d.ts.map +1 -1
  89. package/dist/ui/components.js +11 -11
  90. package/dist/ui/components.js.map +1 -1
  91. package/dist/ui/terminal-header.js +14 -14
  92. package/package.json +105 -105
  93. package/src/ai/__tests__/gemini-analyzer.test.ts +180 -180
  94. package/src/ai/agentkit-orchestrator.ts +533 -533
  95. package/src/ai/fix-manager.ts +362 -362
  96. package/src/ai/gemini-analyzer.ts +665 -671
  97. package/src/ai/gemini-code-fixer.ts +539 -540
  98. package/src/ai/index.ts +3 -3
  99. package/src/ai/jules-implementer.ts +504 -460
  100. package/src/ai/unified-code-fixer.ts +347 -347
  101. package/src/commands/automation.ts +343 -343
  102. package/src/commands/check.ts +298 -299
  103. package/src/commands/config.ts +584 -583
  104. package/src/commands/fix.ts +264 -238
  105. package/src/commands/index.ts +6 -6
  106. package/src/commands/init.ts +155 -155
  107. package/src/commands/status.ts +306 -306
  108. package/src/core/api-key-manager.ts +298 -298
  109. package/src/core/baseguard.ts +757 -756
  110. package/src/core/baseline-checker.ts +564 -563
  111. package/src/core/cache-manager.ts +271 -271
  112. package/src/core/configuration-recovery.ts +672 -673
  113. package/src/core/configuration.ts +595 -595
  114. package/src/core/debug-logger.ts +590 -590
  115. package/src/core/directory-filter.ts +420 -420
  116. package/src/core/error-handler.ts +518 -517
  117. package/src/core/file-processor.ts +337 -337
  118. package/src/core/gitignore-manager.ts +168 -168
  119. package/src/core/graceful-degradation-manager.ts +596 -596
  120. package/src/core/index.ts +16 -16
  121. package/src/core/lazy-loader.ts +317 -307
  122. package/src/core/memory-manager.ts +290 -295
  123. package/src/core/parser-worker.ts +33 -0
  124. package/src/core/startup-optimizer.ts +246 -255
  125. package/src/core/system-error-handler.ts +755 -756
  126. package/src/git/automation-engine.ts +361 -361
  127. package/src/git/github-manager.ts +190 -192
  128. package/src/git/hook-manager.ts +210 -210
  129. package/src/git/index.ts +3 -3
  130. package/src/index.ts +7 -7
  131. package/src/parsers/feature-validator.ts +558 -558
  132. package/src/parsers/index.ts +7 -7
  133. package/src/parsers/parser-manager.ts +418 -419
  134. package/src/parsers/parser.ts +25 -25
  135. package/src/parsers/react-parser-optimized.ts +160 -160
  136. package/src/parsers/react-parser.ts +358 -358
  137. package/src/parsers/svelte-parser.ts +510 -510
  138. package/src/parsers/vanilla-parser.ts +685 -686
  139. package/src/parsers/vue-parser.ts +476 -478
  140. package/src/types/index.ts +95 -95
  141. package/src/ui/components.ts +567 -567
  142. package/src/ui/help.ts +192 -192
  143. package/src/ui/index.ts +3 -3
  144. package/src/ui/prompts.ts +680 -680
  145. package/src/ui/terminal-header.ts +58 -58
  146. package/test-build.js +40 -40
  147. package/test-config-commands.js +55 -55
  148. package/test-header-simple.js +32 -32
  149. package/test-terminal-header.js +11 -11
  150. package/test-ui.js +28 -28
  151. package/tests/e2e/baseguard.e2e.test.ts +515 -515
  152. package/tests/e2e/cross-platform.e2e.test.ts +419 -419
  153. package/tests/e2e/git-integration.e2e.test.ts +486 -486
  154. package/tests/fixtures/react-project/package.json +13 -13
  155. package/tests/fixtures/react-project/src/App.css +75 -75
  156. package/tests/fixtures/react-project/src/App.tsx +76 -76
  157. package/tests/fixtures/svelte-project/package.json +10 -10
  158. package/tests/fixtures/svelte-project/src/App.svelte +368 -368
  159. package/tests/fixtures/vanilla-project/index.html +75 -75
  160. package/tests/fixtures/vanilla-project/script.js +330 -330
  161. package/tests/fixtures/vanilla-project/styles.css +358 -358
  162. package/tests/fixtures/vue-project/package.json +11 -11
  163. package/tests/fixtures/vue-project/src/App.vue +215 -215
  164. package/tsconfig.json +34 -34
  165. package/vitest.config.ts +11 -11
  166. package/dist/terminal-header.d.ts +0 -12
  167. package/dist/terminal-header.js +0 -45
@@ -1,419 +1,418 @@
1
- import type { DetectedFeature } from '../types/index.js';
2
- import { Parser } from './parser.js';
3
- import { FeatureValidator } from './feature-validator.js';
4
- import { LazyLoader } from '../core/lazy-loader.js';
5
- import { MemoryManager } from '../core/memory-manager.js';
6
- import { readFile, stat } from 'fs/promises';
7
- import { join } from 'path';
8
-
9
- /**
10
- * Parser manager that coordinates all parsers and provides concurrent processing
11
- * for large codebases with advanced web APIs using lazy loading
12
- */
13
- export class ParserManager {
14
- private parsers: Map<string, Parser> = new Map();
15
- private readonly validator: FeatureValidator;
16
- private readonly maxConcurrency: number;
17
- private initialized = false;
18
-
19
- constructor(maxConcurrency: number = 10) {
20
- this.validator = new FeatureValidator();
21
- this.maxConcurrency = maxConcurrency;
22
- // Don't initialize parsers immediately - use lazy loading
23
- }
24
-
25
- /**
26
- * Lazy load parsers only when needed
27
- */
28
- private async ensureParsersLoaded(): Promise<void> {
29
- if (this.initialized) {
30
- return;
31
- }
32
-
33
- try {
34
- // Dynamically import parsers to reduce startup time
35
- const [
36
- { ReactParser },
37
- { VueParser },
38
- { SvelteParser },
39
- { VanillaParser }
40
- ] = await Promise.all([
41
- import('./react-parser.js'),
42
- import('./vue-parser.js'),
43
- import('./svelte-parser.js'),
44
- import('./vanilla-parser.js')
45
- ]);
46
-
47
- this.parsers.set('react', new ReactParser());
48
- this.parsers.set('vue', new VueParser());
49
- this.parsers.set('svelte', new SvelteParser());
50
- this.parsers.set('vanilla', new VanillaParser());
51
-
52
- this.initialized = true;
53
- } catch (error) {
54
- console.warn('Failed to load some parsers:', error);
55
- this.initialized = true;
56
- }
57
- }
58
-
59
- /**
60
- * Get parser for file type with lazy loading
61
- */
62
- private async getParserForFile(filePath: string): Promise<Parser | null> {
63
- await this.ensureParsersLoaded();
64
-
65
- for (const parser of this.parsers.values()) {
66
- if (parser.canParse(filePath)) {
67
- return parser;
68
- }
69
- }
70
-
71
- return null;
72
- }
73
-
74
- /**
75
- * Parse multiple files concurrently with error handling
76
- */
77
- async parseFiles(filePaths: string[]): Promise<DetectedFeature[]> {
78
- const allFeatures: DetectedFeature[] = [];
79
-
80
- // Filter out unsupported files
81
- const supportedFiles = filePaths.filter(filePath => this.canParseFile(filePath));
82
-
83
- if (supportedFiles.length === 0) {
84
- return [];
85
- }
86
-
87
- // Process files in batches for better performance
88
- const batches = this.createBatches(supportedFiles, this.maxConcurrency);
89
-
90
- for (const batch of batches) {
91
- try {
92
- const batchResults = await Promise.allSettled(
93
- batch.map(filePath => this.parseFile(filePath))
94
- );
95
-
96
- batchResults.forEach((result, index) => {
97
- if (result.status === 'fulfilled') {
98
- allFeatures.push(...result.value);
99
- } else {
100
- console.warn(`Failed to parse ${batch[index]}: ${result.reason}`);
101
- }
102
- });
103
-
104
- // Small delay between batches to prevent overwhelming the system
105
- if (batches.length > 1) {
106
- await this.sleep(10);
107
- }
108
-
109
- } catch (error) {
110
- console.error(`Error processing batch: ${error instanceof Error ? error.message : 'Unknown error'}`);
111
- }
112
- }
113
-
114
- // Validate and filter features
115
- return this.validator.validateFeatures(allFeatures, this.maxConcurrency);
116
- }
117
-
118
- /**
119
- * Parse a single file with memory optimization
120
- */
121
- async parseFile(filePath: string): Promise<DetectedFeature[]> {
122
- try {
123
- // Find appropriate parser
124
- const parser = await this.getParserForFile(filePath);
125
- if (!parser) {
126
- return [];
127
- }
128
-
129
- // Check file size and use streaming for large files
130
- const fileStats = await stat(filePath);
131
-
132
- if (MemoryManager.shouldStream(fileStats.size)) {
133
- return await this.parseFileStreaming(filePath, parser);
134
- }
135
-
136
- // Read file content for smaller files
137
- const content = await readFile(filePath, 'utf-8');
138
-
139
- // Parse features
140
- const features = await parser.parseFeatures(content, filePath);
141
-
142
- // Add file path to features for tracking and optimize memory
143
- return features.map(feature => MemoryManager.optimizeObject({
144
- ...feature,
145
- file: filePath
146
- }));
147
-
148
- } catch (error) {
149
- // Graceful error handling for unsupported files and syntax errors
150
- if (error instanceof Error) {
151
- if (error.message.includes('ENOENT')) {
152
- console.warn(`File not found: ${filePath}`);
153
- } else if (error.message.includes('SyntaxError') || error.message.includes('Unexpected token')) {
154
- console.warn(`Syntax error in ${filePath}: ${error.message}`);
155
- } else {
156
- console.warn(`Error parsing ${filePath}: ${error.message}`);
157
- }
158
- } else {
159
- console.warn(`Unknown error parsing ${filePath}`);
160
- }
161
- return [];
162
- }
163
- }
164
-
165
- /**
166
- * Parse large files using streaming to reduce memory usage
167
- */
168
- private async parseFileStreaming(filePath: string, parser: Parser): Promise<DetectedFeature[]> {
169
- const features: DetectedFeature[] = [];
170
-
171
- await MemoryManager.readFileStreaming(filePath, async (chunk, startLine) => {
172
- try {
173
- const chunkFeatures = await parser.parseFeatures(chunk, filePath);
174
-
175
- // Adjust line numbers for chunk offset
176
- const adjustedFeatures = chunkFeatures.map(feature => ({
177
- ...feature,
178
- line: feature.line + startLine - 1,
179
- file: filePath
180
- }));
181
-
182
- features.push(...adjustedFeatures);
183
- } catch (error) {
184
- // Continue processing other chunks even if one fails
185
- console.warn(`Error parsing chunk in ${filePath}: ${error}`);
186
- }
187
- });
188
-
189
- return features;
190
- }
191
-
192
- /**
193
- * Check if a file can be parsed by any available parser
194
- */
195
- async canParseFile(filePath: string): Promise<boolean> {
196
- await this.ensureParsersLoaded();
197
-
198
- for (const parser of this.parsers.values()) {
199
- if (parser.canParse(filePath)) {
200
- return true;
201
- }
202
- }
203
-
204
- return false;
205
- }
206
-
207
- /**
208
- * Get all supported file extensions
209
- */
210
- async getSupportedExtensions(): Promise<string[]> {
211
- await this.ensureParsersLoaded();
212
-
213
- const extensions = new Set<string>();
214
- this.parsers.forEach(parser => {
215
- parser.getSupportedExtensions().forEach(ext => extensions.add(ext));
216
- });
217
- return Array.from(extensions);
218
- }
219
-
220
- /**
221
- * Get parser information for debugging
222
- */
223
- async getParserInfo(): Promise<Array<{ name: string; extensions: string[] }>> {
224
- await this.ensureParsersLoaded();
225
-
226
- return Array.from(this.parsers.values()).map(parser => ({
227
- name: parser.getName(),
228
- extensions: parser.getSupportedExtensions()
229
- }));
230
- }
231
-
232
- /**
233
- * Filter files by supported extensions
234
- */
235
- async filterSupportedFiles(filePaths: string[]): Promise<string[]> {
236
- const supportedExtensions = await this.getSupportedExtensions();
237
- return filePaths.filter(filePath => {
238
- const extension = this.getFileExtension(filePath);
239
- return supportedExtensions.includes(extension);
240
- });
241
- }
242
-
243
- /**
244
- * Scan directory for supported files (recursive)
245
- */
246
- async scanDirectory(
247
- directoryPath: string,
248
- options: {
249
- recursive?: boolean;
250
- excludePatterns?: string[];
251
- maxDepth?: number;
252
- } = {}
253
- ): Promise<string[]> {
254
- const {
255
- recursive = true,
256
- excludePatterns = ['node_modules', '.git', 'dist', 'build', '.next', '.nuxt'],
257
- maxDepth = 10
258
- } = options;
259
-
260
- const supportedFiles: string[] = [];
261
-
262
- try {
263
- await this.scanDirectoryRecursive(
264
- directoryPath,
265
- supportedFiles,
266
- excludePatterns,
267
- recursive,
268
- 0,
269
- maxDepth
270
- );
271
- } catch (error) {
272
- console.error(`Error scanning directory ${directoryPath}: ${error instanceof Error ? error.message : 'Unknown error'}`);
273
- }
274
-
275
- return supportedFiles;
276
- }
277
-
278
- /**
279
- * Recursive directory scanning helper
280
- */
281
- private async scanDirectoryRecursive(
282
- currentPath: string,
283
- results: string[],
284
- excludePatterns: string[],
285
- recursive: boolean,
286
- currentDepth: number,
287
- maxDepth: number
288
- ): Promise<void> {
289
- if (currentDepth >= maxDepth) {
290
- return;
291
- }
292
-
293
- try {
294
- const { readdir, stat } = await import('fs/promises');
295
- const entries = await readdir(currentPath);
296
-
297
- for (const entry of entries) {
298
- const fullPath = join(currentPath, entry);
299
-
300
- // Skip excluded patterns
301
- if (excludePatterns.some(pattern => entry.includes(pattern))) {
302
- continue;
303
- }
304
-
305
- try {
306
- const stats = await stat(fullPath);
307
-
308
- if (stats.isFile()) {
309
- if (await this.canParseFile(fullPath)) {
310
- results.push(fullPath);
311
- }
312
- } else if (stats.isDirectory() && recursive) {
313
- await this.scanDirectoryRecursive(
314
- fullPath,
315
- results,
316
- excludePatterns,
317
- recursive,
318
- currentDepth + 1,
319
- maxDepth
320
- );
321
- }
322
- } catch (error) {
323
- // Skip files/directories that can't be accessed
324
- continue;
325
- }
326
- }
327
- } catch (error) {
328
- console.warn(`Could not read directory ${currentPath}: ${error instanceof Error ? error.message : 'Unknown error'}`);
329
- }
330
- }
331
-
332
- /**
333
- * Get processing statistics
334
- */
335
- async getProcessingStats(filePaths: string[]): Promise<{
336
- totalFiles: number;
337
- supportedFiles: number;
338
- filesByType: Record<string, number>;
339
- estimatedProcessingTime: number;
340
- memoryEstimate: string;
341
- }> {
342
- const supportedFiles = await this.filterSupportedFiles(filePaths);
343
- const filesByType: Record<string, number> = {};
344
-
345
- supportedFiles.forEach(filePath => {
346
- const extension = this.getFileExtension(filePath);
347
- filesByType[extension] = (filesByType[extension] || 0) + 1;
348
- });
349
-
350
- // Rough estimation: 10ms per file on average
351
- const estimatedProcessingTime = Math.ceil(supportedFiles.length * 10 / this.maxConcurrency);
352
-
353
- // Memory estimate: ~50KB per file on average
354
- const memoryEstimate = `${Math.round(supportedFiles.length * 50 / 1024)}MB`;
355
-
356
- return {
357
- totalFiles: filePaths.length,
358
- supportedFiles: supportedFiles.length,
359
- filesByType,
360
- estimatedProcessingTime,
361
- memoryEstimate
362
- };
363
- }
364
-
365
- /**
366
- * Get memory and performance statistics
367
- */
368
- getStats(): {
369
- parsersLoaded: number;
370
- maxConcurrency: number;
371
- memoryStats: any;
372
- } {
373
- return {
374
- parsersLoaded: this.parsers.size,
375
- maxConcurrency: this.maxConcurrency,
376
- memoryStats: MemoryManager.getMemoryStats()
377
- };
378
- }
379
-
380
- /**
381
- * Create batches for concurrent processing
382
- */
383
- private createBatches<T>(items: T[], batchSize: number): T[][] {
384
- const batches: T[][] = [];
385
- for (let i = 0; i < items.length; i += batchSize) {
386
- batches.push(items.slice(i, i + batchSize));
387
- }
388
- return batches;
389
- }
390
-
391
- /**
392
- * Get file extension
393
- */
394
- private getFileExtension(filePath: string): string {
395
- const match = filePath.match(/\.[^.]+$/);
396
- return match ? match[0] : '';
397
- }
398
-
399
- /**
400
- * Sleep utility for batch processing
401
- */
402
- private sleep(ms: number): Promise<void> {
403
- return new Promise(resolve => setTimeout(resolve, ms));
404
- }
405
-
406
- /**
407
- * Get feature validator instance
408
- */
409
- getValidator(): FeatureValidator {
410
- return this.validator;
411
- }
412
-
413
- /**
414
- * Validate features without parsing (for external use)
415
- */
416
- async validateFeatures(features: DetectedFeature[]): Promise<DetectedFeature[]> {
417
- return this.validator.validateFeatures(features, this.maxConcurrency);
418
- }
419
- }
1
+ import type { DetectedFeature } from '../types/index.js';
2
+ import { Parser } from './parser.js';
3
+ import { FeatureValidator } from './feature-validator.js';
4
+ import { MemoryManager } from '../core/memory-manager.js';
5
+ import { readFile, stat } from 'fs/promises';
6
+ import { join } from 'path';
7
+
8
+ /**
9
+ * Parser manager that coordinates all parsers and provides concurrent processing
10
+ * for large codebases with advanced web APIs using lazy loading
11
+ */
12
+ export class ParserManager {
13
+ private parsers: Map<string, Parser> = new Map();
14
+ private readonly validator: FeatureValidator;
15
+ private readonly maxConcurrency: number;
16
+ private initialized = false;
17
+
18
+ constructor(maxConcurrency: number = 10) {
19
+ this.validator = new FeatureValidator();
20
+ this.maxConcurrency = maxConcurrency;
21
+ // Don't initialize parsers immediately - use lazy loading
22
+ }
23
+
24
+ /**
25
+ * Lazy load parsers only when needed
26
+ */
27
+ private async ensureParsersLoaded(): Promise<void> {
28
+ if (this.initialized) {
29
+ return;
30
+ }
31
+
32
+ try {
33
+ // Dynamically import parsers to reduce startup time
34
+ const [
35
+ { ReactParser },
36
+ { VueParser },
37
+ { SvelteParser },
38
+ { VanillaParser }
39
+ ] = await Promise.all([
40
+ import('./react-parser.js'),
41
+ import('./vue-parser.js'),
42
+ import('./svelte-parser.js'),
43
+ import('./vanilla-parser.js')
44
+ ]);
45
+
46
+ this.parsers.set('react', new ReactParser());
47
+ this.parsers.set('vue', new VueParser());
48
+ this.parsers.set('svelte', new SvelteParser());
49
+ this.parsers.set('vanilla', new VanillaParser());
50
+
51
+ this.initialized = true;
52
+ } catch (error) {
53
+ console.warn('Failed to load some parsers:', error);
54
+ this.initialized = true;
55
+ }
56
+ }
57
+
58
+ /**
59
+ * Get parser for file type with lazy loading
60
+ */
61
+ private async getParserForFile(filePath: string): Promise<Parser | null> {
62
+ await this.ensureParsersLoaded();
63
+
64
+ for (const parser of this.parsers.values()) {
65
+ if (parser.canParse(filePath)) {
66
+ return parser;
67
+ }
68
+ }
69
+
70
+ return null;
71
+ }
72
+
73
+ /**
74
+ * Parse multiple files concurrently with error handling
75
+ */
76
+ async parseFiles(filePaths: string[]): Promise<DetectedFeature[]> {
77
+ const allFeatures: DetectedFeature[] = [];
78
+
79
+ // Filter out unsupported files
80
+ const supportedFiles = await this.filterSupportedFiles(filePaths);
81
+
82
+ if (supportedFiles.length === 0) {
83
+ return [];
84
+ }
85
+
86
+ // Process files in batches for better performance
87
+ const batches = this.createBatches(supportedFiles, this.maxConcurrency);
88
+
89
+ for (const batch of batches) {
90
+ try {
91
+ const batchResults = await Promise.allSettled(
92
+ batch.map(filePath => this.parseFile(filePath))
93
+ );
94
+
95
+ batchResults.forEach((result, index) => {
96
+ if (result.status === 'fulfilled') {
97
+ allFeatures.push(...result.value);
98
+ } else {
99
+ console.warn(`Failed to parse ${batch[index]}: ${result.reason}`);
100
+ }
101
+ });
102
+
103
+ // Small delay between batches to prevent overwhelming the system
104
+ if (batches.length > 1) {
105
+ await this.sleep(10);
106
+ }
107
+
108
+ } catch (error) {
109
+ console.error(`Error processing batch: ${error instanceof Error ? error.message : 'Unknown error'}`);
110
+ }
111
+ }
112
+
113
+ // Validate and filter features
114
+ return this.validator.validateFeatures(allFeatures, this.maxConcurrency);
115
+ }
116
+
117
+ /**
118
+ * Parse a single file with memory optimization
119
+ */
120
+ async parseFile(filePath: string): Promise<DetectedFeature[]> {
121
+ try {
122
+ // Find appropriate parser
123
+ const parser = await this.getParserForFile(filePath);
124
+ if (!parser) {
125
+ return [];
126
+ }
127
+
128
+ // Check file size and use streaming for large files
129
+ const fileStats = await stat(filePath);
130
+
131
+ if (MemoryManager.shouldStream(fileStats.size)) {
132
+ return await this.parseFileStreaming(filePath, parser);
133
+ }
134
+
135
+ // Read file content for smaller files
136
+ const content = await readFile(filePath, 'utf-8');
137
+
138
+ // Parse features
139
+ const features = await parser.parseFeatures(content, filePath);
140
+
141
+ // Add file path to features for tracking and optimize memory
142
+ return features.map(feature => MemoryManager.optimizeObject({
143
+ ...feature,
144
+ file: filePath
145
+ }));
146
+
147
+ } catch (error) {
148
+ // Graceful error handling for unsupported files and syntax errors
149
+ if (error instanceof Error) {
150
+ if (error.message.includes('ENOENT')) {
151
+ console.warn(`File not found: ${filePath}`);
152
+ } else if (error.message.includes('SyntaxError') || error.message.includes('Unexpected token')) {
153
+ console.warn(`Syntax error in ${filePath}: ${error.message}`);
154
+ } else {
155
+ console.warn(`Error parsing ${filePath}: ${error.message}`);
156
+ }
157
+ } else {
158
+ console.warn(`Unknown error parsing ${filePath}`);
159
+ }
160
+ return [];
161
+ }
162
+ }
163
+
164
+ /**
165
+ * Parse large files using streaming to reduce memory usage
166
+ */
167
+ private async parseFileStreaming(filePath: string, parser: Parser): Promise<DetectedFeature[]> {
168
+ const features: DetectedFeature[] = [];
169
+
170
+ await MemoryManager.readFileStreaming(filePath, async (chunk, startLine) => {
171
+ try {
172
+ const chunkFeatures = await parser.parseFeatures(chunk, filePath);
173
+
174
+ // Adjust line numbers for chunk offset
175
+ const adjustedFeatures = chunkFeatures.map(feature => ({
176
+ ...feature,
177
+ line: feature.line + startLine - 1,
178
+ file: filePath
179
+ }));
180
+
181
+ features.push(...adjustedFeatures);
182
+ } catch (error) {
183
+ // Continue processing other chunks even if one fails
184
+ console.warn(`Error parsing chunk in ${filePath}: ${error}`);
185
+ }
186
+ });
187
+
188
+ return features;
189
+ }
190
+
191
+ /**
192
+ * Check if a file can be parsed by any available parser
193
+ */
194
+ async canParseFile(filePath: string): Promise<boolean> {
195
+ await this.ensureParsersLoaded();
196
+
197
+ for (const parser of this.parsers.values()) {
198
+ if (parser.canParse(filePath)) {
199
+ return true;
200
+ }
201
+ }
202
+
203
+ return false;
204
+ }
205
+
206
+ /**
207
+ * Get all supported file extensions
208
+ */
209
+ async getSupportedExtensions(): Promise<string[]> {
210
+ await this.ensureParsersLoaded();
211
+
212
+ const extensions = new Set<string>();
213
+ this.parsers.forEach(parser => {
214
+ parser.getSupportedExtensions().forEach(ext => extensions.add(ext));
215
+ });
216
+ return Array.from(extensions);
217
+ }
218
+
219
+ /**
220
+ * Get parser information for debugging
221
+ */
222
+ async getParserInfo(): Promise<Array<{ name: string; extensions: string[] }>> {
223
+ await this.ensureParsersLoaded();
224
+
225
+ return Array.from(this.parsers.values()).map(parser => ({
226
+ name: parser.getName(),
227
+ extensions: parser.getSupportedExtensions()
228
+ }));
229
+ }
230
+
231
+ /**
232
+ * Filter files by supported extensions
233
+ */
234
+ async filterSupportedFiles(filePaths: string[]): Promise<string[]> {
235
+ const supportedExtensions = await this.getSupportedExtensions();
236
+ return filePaths.filter(filePath => {
237
+ const extension = this.getFileExtension(filePath);
238
+ return supportedExtensions.includes(extension);
239
+ });
240
+ }
241
+
242
+ /**
243
+ * Scan directory for supported files (recursive)
244
+ */
245
+ async scanDirectory(
246
+ directoryPath: string,
247
+ options: {
248
+ recursive?: boolean;
249
+ excludePatterns?: string[];
250
+ maxDepth?: number;
251
+ } = {}
252
+ ): Promise<string[]> {
253
+ const {
254
+ recursive = true,
255
+ excludePatterns = ['node_modules', '.git', 'dist', 'build', '.next', '.nuxt'],
256
+ maxDepth = 10
257
+ } = options;
258
+
259
+ const supportedFiles: string[] = [];
260
+
261
+ try {
262
+ await this.scanDirectoryRecursive(
263
+ directoryPath,
264
+ supportedFiles,
265
+ excludePatterns,
266
+ recursive,
267
+ 0,
268
+ maxDepth
269
+ );
270
+ } catch (error) {
271
+ console.error(`Error scanning directory ${directoryPath}: ${error instanceof Error ? error.message : 'Unknown error'}`);
272
+ }
273
+
274
+ return supportedFiles;
275
+ }
276
+
277
+ /**
278
+ * Recursive directory scanning helper
279
+ */
280
+ private async scanDirectoryRecursive(
281
+ currentPath: string,
282
+ results: string[],
283
+ excludePatterns: string[],
284
+ recursive: boolean,
285
+ currentDepth: number,
286
+ maxDepth: number
287
+ ): Promise<void> {
288
+ if (currentDepth >= maxDepth) {
289
+ return;
290
+ }
291
+
292
+ try {
293
+ const { readdir, stat } = await import('fs/promises');
294
+ const entries = await readdir(currentPath);
295
+
296
+ for (const entry of entries) {
297
+ const fullPath = join(currentPath, entry);
298
+
299
+ // Skip excluded patterns
300
+ if (excludePatterns.some(pattern => entry.includes(pattern))) {
301
+ continue;
302
+ }
303
+
304
+ try {
305
+ const stats = await stat(fullPath);
306
+
307
+ if (stats.isFile()) {
308
+ if (await this.canParseFile(fullPath)) {
309
+ results.push(fullPath);
310
+ }
311
+ } else if (stats.isDirectory() && recursive) {
312
+ await this.scanDirectoryRecursive(
313
+ fullPath,
314
+ results,
315
+ excludePatterns,
316
+ recursive,
317
+ currentDepth + 1,
318
+ maxDepth
319
+ );
320
+ }
321
+ } catch (error) {
322
+ // Skip files/directories that can't be accessed
323
+ continue;
324
+ }
325
+ }
326
+ } catch (error) {
327
+ console.warn(`Could not read directory ${currentPath}: ${error instanceof Error ? error.message : 'Unknown error'}`);
328
+ }
329
+ }
330
+
331
+ /**
332
+ * Get processing statistics
333
+ */
334
+ async getProcessingStats(filePaths: string[]): Promise<{
335
+ totalFiles: number;
336
+ supportedFiles: number;
337
+ filesByType: Record<string, number>;
338
+ estimatedProcessingTime: number;
339
+ memoryEstimate: string;
340
+ }> {
341
+ const supportedFiles = await this.filterSupportedFiles(filePaths);
342
+ const filesByType: Record<string, number> = {};
343
+
344
+ supportedFiles.forEach(filePath => {
345
+ const extension = this.getFileExtension(filePath);
346
+ filesByType[extension] = (filesByType[extension] || 0) + 1;
347
+ });
348
+
349
+ // Rough estimation: 10ms per file on average
350
+ const estimatedProcessingTime = Math.ceil(supportedFiles.length * 10 / this.maxConcurrency);
351
+
352
+ // Memory estimate: ~50KB per file on average
353
+ const memoryEstimate = `${Math.round(supportedFiles.length * 50 / 1024)}MB`;
354
+
355
+ return {
356
+ totalFiles: filePaths.length,
357
+ supportedFiles: supportedFiles.length,
358
+ filesByType,
359
+ estimatedProcessingTime,
360
+ memoryEstimate
361
+ };
362
+ }
363
+
364
+ /**
365
+ * Get memory and performance statistics
366
+ */
367
+ getStats(): {
368
+ parsersLoaded: number;
369
+ maxConcurrency: number;
370
+ memoryStats: any;
371
+ } {
372
+ return {
373
+ parsersLoaded: this.parsers.size,
374
+ maxConcurrency: this.maxConcurrency,
375
+ memoryStats: MemoryManager.getMemoryStats()
376
+ };
377
+ }
378
+
379
+ /**
380
+ * Create batches for concurrent processing
381
+ */
382
+ private createBatches<T>(items: T[], batchSize: number): T[][] {
383
+ const batches: T[][] = [];
384
+ for (let i = 0; i < items.length; i += batchSize) {
385
+ batches.push(items.slice(i, i + batchSize));
386
+ }
387
+ return batches;
388
+ }
389
+
390
+ /**
391
+ * Get file extension
392
+ */
393
+ private getFileExtension(filePath: string): string {
394
+ const match = filePath.match(/\.[^.]+$/);
395
+ return match ? match[0] : '';
396
+ }
397
+
398
+ /**
399
+ * Sleep utility for batch processing
400
+ */
401
+ private sleep(ms: number): Promise<void> {
402
+ return new Promise(resolve => setTimeout(resolve, ms));
403
+ }
404
+
405
+ /**
406
+ * Get feature validator instance
407
+ */
408
+ getValidator(): FeatureValidator {
409
+ return this.validator;
410
+ }
411
+
412
+ /**
413
+ * Validate features without parsing (for external use)
414
+ */
415
+ async validateFeatures(features: DetectedFeature[]): Promise<DetectedFeature[]> {
416
+ return this.validator.validateFeatures(features, this.maxConcurrency);
417
+ }
418
+ }