test-genie-mcp 2.0.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.
Files changed (102) hide show
  1. package/README.md +219 -0
  2. package/dist/analyzers/astAnalyzer.d.ts +108 -0
  3. package/dist/analyzers/astAnalyzer.d.ts.map +1 -0
  4. package/dist/analyzers/astAnalyzer.js +800 -0
  5. package/dist/analyzers/astAnalyzer.js.map +1 -0
  6. package/dist/analyzers/performanceAnalyzer.d.ts +90 -0
  7. package/dist/analyzers/performanceAnalyzer.d.ts.map +1 -0
  8. package/dist/analyzers/performanceAnalyzer.js +647 -0
  9. package/dist/analyzers/performanceAnalyzer.js.map +1 -0
  10. package/dist/index.d.ts +3 -0
  11. package/dist/index.d.ts.map +1 -0
  12. package/dist/index.js +683 -0
  13. package/dist/index.js.map +1 -0
  14. package/dist/platforms/android/index.d.ts +90 -0
  15. package/dist/platforms/android/index.d.ts.map +1 -0
  16. package/dist/platforms/android/index.js +519 -0
  17. package/dist/platforms/android/index.js.map +1 -0
  18. package/dist/platforms/flutter/index.d.ts +105 -0
  19. package/dist/platforms/flutter/index.d.ts.map +1 -0
  20. package/dist/platforms/flutter/index.js +533 -0
  21. package/dist/platforms/flutter/index.js.map +1 -0
  22. package/dist/platforms/ios/index.d.ts +79 -0
  23. package/dist/platforms/ios/index.d.ts.map +1 -0
  24. package/dist/platforms/ios/index.js +393 -0
  25. package/dist/platforms/ios/index.js.map +1 -0
  26. package/dist/platforms/react-native/index.d.ts +160 -0
  27. package/dist/platforms/react-native/index.d.ts.map +1 -0
  28. package/dist/platforms/react-native/index.js +602 -0
  29. package/dist/platforms/react-native/index.js.map +1 -0
  30. package/dist/platforms/web/index.d.ts +177 -0
  31. package/dist/platforms/web/index.d.ts.map +1 -0
  32. package/dist/platforms/web/index.js +637 -0
  33. package/dist/platforms/web/index.js.map +1 -0
  34. package/dist/storage/index.d.ts +35 -0
  35. package/dist/storage/index.d.ts.map +1 -0
  36. package/dist/storage/index.js +351 -0
  37. package/dist/storage/index.js.map +1 -0
  38. package/dist/tools/analysis/analyzeAppStructure.d.ts +9 -0
  39. package/dist/tools/analysis/analyzeAppStructure.d.ts.map +1 -0
  40. package/dist/tools/analysis/analyzeAppStructure.js +289 -0
  41. package/dist/tools/analysis/analyzeAppStructure.js.map +1 -0
  42. package/dist/tools/analysis/createTestPlan.d.ts +21 -0
  43. package/dist/tools/analysis/createTestPlan.d.ts.map +1 -0
  44. package/dist/tools/analysis/createTestPlan.js +214 -0
  45. package/dist/tools/analysis/createTestPlan.js.map +1 -0
  46. package/dist/tools/analysis/generateScenarios.d.ts +21 -0
  47. package/dist/tools/analysis/generateScenarios.d.ts.map +1 -0
  48. package/dist/tools/analysis/generateScenarios.js +567 -0
  49. package/dist/tools/analysis/generateScenarios.js.map +1 -0
  50. package/dist/tools/automation/cicdIntegration.d.ts +41 -0
  51. package/dist/tools/automation/cicdIntegration.d.ts.map +1 -0
  52. package/dist/tools/automation/cicdIntegration.js +825 -0
  53. package/dist/tools/automation/cicdIntegration.js.map +1 -0
  54. package/dist/tools/automation/generateReport.d.ts +16 -0
  55. package/dist/tools/automation/generateReport.d.ts.map +1 -0
  56. package/dist/tools/automation/generateReport.js +406 -0
  57. package/dist/tools/automation/generateReport.js.map +1 -0
  58. package/dist/tools/automation/runFullAutomation.d.ts +25 -0
  59. package/dist/tools/automation/runFullAutomation.d.ts.map +1 -0
  60. package/dist/tools/automation/runFullAutomation.js +296 -0
  61. package/dist/tools/automation/runFullAutomation.js.map +1 -0
  62. package/dist/tools/detection/detectLogicErrors.d.ts +18 -0
  63. package/dist/tools/detection/detectLogicErrors.d.ts.map +1 -0
  64. package/dist/tools/detection/detectLogicErrors.js +470 -0
  65. package/dist/tools/detection/detectLogicErrors.js.map +1 -0
  66. package/dist/tools/detection/detectMemoryLeaks.d.ts +23 -0
  67. package/dist/tools/detection/detectMemoryLeaks.d.ts.map +1 -0
  68. package/dist/tools/detection/detectMemoryLeaks.js +453 -0
  69. package/dist/tools/detection/detectMemoryLeaks.js.map +1 -0
  70. package/dist/tools/execution/runScenarioTest.d.ts +34 -0
  71. package/dist/tools/execution/runScenarioTest.d.ts.map +1 -0
  72. package/dist/tools/execution/runScenarioTest.js +334 -0
  73. package/dist/tools/execution/runScenarioTest.js.map +1 -0
  74. package/dist/tools/execution/runSimulation.d.ts +17 -0
  75. package/dist/tools/execution/runSimulation.d.ts.map +1 -0
  76. package/dist/tools/execution/runSimulation.js +250 -0
  77. package/dist/tools/execution/runSimulation.js.map +1 -0
  78. package/dist/tools/execution/runStressTest.d.ts +52 -0
  79. package/dist/tools/execution/runStressTest.d.ts.map +1 -0
  80. package/dist/tools/execution/runStressTest.js +176 -0
  81. package/dist/tools/execution/runStressTest.js.map +1 -0
  82. package/dist/tools/fixing/applyFix.d.ts +44 -0
  83. package/dist/tools/fixing/applyFix.d.ts.map +1 -0
  84. package/dist/tools/fixing/applyFix.js +399 -0
  85. package/dist/tools/fixing/applyFix.js.map +1 -0
  86. package/dist/tools/fixing/confirmFix.d.ts +31 -0
  87. package/dist/tools/fixing/confirmFix.d.ts.map +1 -0
  88. package/dist/tools/fixing/confirmFix.js +136 -0
  89. package/dist/tools/fixing/confirmFix.js.map +1 -0
  90. package/dist/tools/fixing/suggestFixes.d.ts +23 -0
  91. package/dist/tools/fixing/suggestFixes.d.ts.map +1 -0
  92. package/dist/tools/fixing/suggestFixes.js +349 -0
  93. package/dist/tools/fixing/suggestFixes.js.map +1 -0
  94. package/dist/types.d.ts +322 -0
  95. package/dist/types.d.ts.map +1 -0
  96. package/dist/types.js +6 -0
  97. package/dist/types.js.map +1 -0
  98. package/dist/utils/codeParser.d.ts +12 -0
  99. package/dist/utils/codeParser.d.ts.map +1 -0
  100. package/dist/utils/codeParser.js +487 -0
  101. package/dist/utils/codeParser.js.map +1 -0
  102. package/package.json +68 -0
@@ -0,0 +1,647 @@
1
+ "use strict";
2
+ // ============================================
3
+ // Performance Analyzer
4
+ // Static and runtime performance analysis
5
+ // ============================================
6
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
7
+ if (k2 === undefined) k2 = k;
8
+ var desc = Object.getOwnPropertyDescriptor(m, k);
9
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
10
+ desc = { enumerable: true, get: function() { return m[k]; } };
11
+ }
12
+ Object.defineProperty(o, k2, desc);
13
+ }) : (function(o, m, k, k2) {
14
+ if (k2 === undefined) k2 = k;
15
+ o[k2] = m[k];
16
+ }));
17
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
18
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
19
+ }) : function(o, v) {
20
+ o["default"] = v;
21
+ });
22
+ var __importStar = (this && this.__importStar) || (function () {
23
+ var ownKeys = function(o) {
24
+ ownKeys = Object.getOwnPropertyNames || function (o) {
25
+ var ar = [];
26
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
27
+ return ar;
28
+ };
29
+ return ownKeys(o);
30
+ };
31
+ return function (mod) {
32
+ if (mod && mod.__esModule) return mod;
33
+ var result = {};
34
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
35
+ __setModuleDefault(result, mod);
36
+ return result;
37
+ };
38
+ })();
39
+ Object.defineProperty(exports, "__esModule", { value: true });
40
+ exports.analyzePerformance = analyzePerformance;
41
+ const fs = __importStar(require("fs"));
42
+ const path = __importStar(require("path"));
43
+ const child_process_1 = require("child_process");
44
+ const util_1 = require("util");
45
+ const astAnalyzer_js_1 = require("./astAnalyzer.js");
46
+ const execAsync = (0, util_1.promisify)(child_process_1.exec);
47
+ // ============================================
48
+ // Main Analysis Function
49
+ // ============================================
50
+ async function analyzePerformance(projectPath, platform) {
51
+ const extensions = getExtensionsForPlatform(platform);
52
+ const files = getAllSourceFiles(projectPath, extensions);
53
+ const allIssues = [];
54
+ const rendering = await analyzeRendering(files, platform);
55
+ const computation = await analyzeComputation(files, platform);
56
+ const network = await analyzeNetwork(files);
57
+ const bundle = await analyzeBundle(projectPath, files, platform);
58
+ // Collect all issues
59
+ allIssues.push(...rendering.issues);
60
+ allIssues.push(...computation.issues);
61
+ allIssues.push(...network.issues);
62
+ allIssues.push(...bundle.issues);
63
+ // Calculate performance score
64
+ const criticalCount = allIssues.filter(i => i.type === 'critical').length;
65
+ const majorCount = allIssues.filter(i => i.type === 'major').length;
66
+ const minorCount = allIssues.filter(i => i.type === 'minor').length;
67
+ let score = 100;
68
+ score -= criticalCount * 15;
69
+ score -= majorCount * 5;
70
+ score -= minorCount * 1;
71
+ score = Math.max(0, Math.min(100, score));
72
+ // Generate recommendations
73
+ const recommendations = generateRecommendations(rendering, computation, network, bundle);
74
+ return {
75
+ projectPath,
76
+ platform,
77
+ analyzedAt: new Date().toISOString(),
78
+ summary: {
79
+ criticalIssues: criticalCount,
80
+ majorIssues: majorCount,
81
+ minorIssues: minorCount,
82
+ performanceScore: score,
83
+ },
84
+ rendering,
85
+ computation,
86
+ network,
87
+ bundle,
88
+ recommendations,
89
+ };
90
+ }
91
+ // ============================================
92
+ // Rendering Analysis
93
+ // ============================================
94
+ async function analyzeRendering(files, platform) {
95
+ const issues = [];
96
+ const expensiveComponents = [];
97
+ const memoizationOpportunities = [];
98
+ for (const file of files) {
99
+ try {
100
+ const content = fs.readFileSync(file, 'utf-8');
101
+ // Check for inline object creation in JSX
102
+ if (content.includes('style={{')) {
103
+ const matches = content.matchAll(/style=\{\{[^}]+\}\}/g);
104
+ for (const match of matches) {
105
+ issues.push({
106
+ type: 'major',
107
+ category: 'rendering',
108
+ title: 'Inline style object in render',
109
+ description: 'Creating style objects inline causes new object creation on every render',
110
+ file,
111
+ line: getLineNumber(content, match.index || 0),
112
+ impact: 'Unnecessary re-renders and garbage collection',
113
+ suggestion: 'Move style object outside component or use useMemo',
114
+ estimatedGain: '10-30% render performance improvement',
115
+ });
116
+ }
117
+ }
118
+ // Check for inline arrow functions in JSX
119
+ const inlineArrowRegex = /on\w+=\{\s*\(\s*\)\s*=>/g;
120
+ if (inlineArrowRegex.test(content)) {
121
+ issues.push({
122
+ type: 'major',
123
+ category: 'rendering',
124
+ title: 'Inline arrow function in render',
125
+ description: 'Inline arrow functions create new function instances on every render',
126
+ file,
127
+ impact: 'May cause child component re-renders',
128
+ suggestion: 'Use useCallback to memoize event handlers',
129
+ estimatedGain: 'Prevents unnecessary child re-renders',
130
+ });
131
+ }
132
+ // Check for missing React.memo on large components
133
+ if (platform === 'react-native' || platform === 'web') {
134
+ const componentMatch = content.match(/(?:export\s+)?(?:const|function)\s+(\w+)[^{]*(?:=>|{)[\s\S]{500,}/);
135
+ if (componentMatch && !content.includes('React.memo') && !content.includes('memo(')) {
136
+ const name = componentMatch[1] || 'Component';
137
+ expensiveComponents.push({
138
+ name,
139
+ file,
140
+ reason: 'Large component without memoization',
141
+ });
142
+ memoizationOpportunities.push({
143
+ component: name,
144
+ file,
145
+ suggestion: `Wrap ${name} with React.memo() to prevent unnecessary re-renders`,
146
+ });
147
+ }
148
+ }
149
+ // Check for expensive operations in render
150
+ const expensivePatterns = [
151
+ { pattern: /\.filter\s*\(/, name: 'Array.filter' },
152
+ { pattern: /\.map\s*\(/, name: 'Array.map' },
153
+ { pattern: /\.reduce\s*\(/, name: 'Array.reduce' },
154
+ { pattern: /JSON\.stringify/, name: 'JSON.stringify' },
155
+ { pattern: /JSON\.parse/, name: 'JSON.parse' },
156
+ { pattern: /new Date\s*\(/, name: 'Date creation' },
157
+ ];
158
+ for (const { pattern, name } of expensivePatterns) {
159
+ // Check if inside render/return
160
+ const returnIndex = content.indexOf('return');
161
+ if (returnIndex > 0) {
162
+ const afterReturn = content.substring(returnIndex);
163
+ if (pattern.test(afterReturn)) {
164
+ issues.push({
165
+ type: 'minor',
166
+ category: 'rendering',
167
+ title: `${name} in render path`,
168
+ description: `${name} called during render may impact performance`,
169
+ file,
170
+ impact: 'Computation on every render',
171
+ suggestion: `Move ${name} to useMemo or calculate before render`,
172
+ });
173
+ }
174
+ }
175
+ }
176
+ // Check for missing key prop in lists
177
+ if (content.includes('.map') && !content.includes('key=')) {
178
+ issues.push({
179
+ type: 'major',
180
+ category: 'rendering',
181
+ title: 'Missing key prop in list rendering',
182
+ description: 'List items without key prop cause inefficient reconciliation',
183
+ file,
184
+ impact: 'React cannot efficiently update list items',
185
+ suggestion: 'Add unique key prop to each list item',
186
+ });
187
+ }
188
+ // Flutter-specific checks
189
+ if (platform === 'flutter') {
190
+ // Check for rebuild on every setState
191
+ if (content.includes('setState') && !content.includes('const ')) {
192
+ issues.push({
193
+ type: 'minor',
194
+ category: 'rendering',
195
+ title: 'setState may cause full widget rebuild',
196
+ description: 'Consider using const constructors to prevent rebuilds',
197
+ file,
198
+ impact: 'Unnecessary widget rebuilds',
199
+ suggestion: 'Use const constructors where possible',
200
+ });
201
+ }
202
+ // Check for missing const in widget constructors
203
+ const widgetRegex = /return\s+(?!const\s+)\w+\s*\(/g;
204
+ if (widgetRegex.test(content)) {
205
+ issues.push({
206
+ type: 'minor',
207
+ category: 'rendering',
208
+ title: 'Widget without const constructor',
209
+ description: 'Non-const widgets are rebuilt on every parent rebuild',
210
+ file,
211
+ impact: 'Unnecessary widget rebuilds',
212
+ suggestion: 'Use const constructors for stateless widgets',
213
+ });
214
+ }
215
+ }
216
+ }
217
+ catch {
218
+ // Skip files that can't be read
219
+ }
220
+ }
221
+ return { issues, expensiveComponents, memoizationOpportunities };
222
+ }
223
+ // ============================================
224
+ // Computation Analysis
225
+ // ============================================
226
+ async function analyzeComputation(files, platform) {
227
+ const issues = [];
228
+ const heavyFunctions = [];
229
+ const optimizationOpportunities = [];
230
+ for (const file of files) {
231
+ try {
232
+ const content = fs.readFileSync(file, 'utf-8');
233
+ const analysis = (0, astAnalyzer_js_1.analyzeTypeScript)(file, content);
234
+ // Check for high complexity functions
235
+ for (const func of analysis.functions) {
236
+ if (func.complexity > 10) {
237
+ heavyFunctions.push({
238
+ name: func.name,
239
+ file,
240
+ complexity: func.complexity,
241
+ suggestion: 'Consider breaking into smaller functions',
242
+ });
243
+ if (func.complexity > 20) {
244
+ issues.push({
245
+ type: 'major',
246
+ category: 'computation',
247
+ title: `High complexity function: ${func.name}`,
248
+ description: `Cyclomatic complexity of ${func.complexity} exceeds recommended threshold`,
249
+ file,
250
+ line: func.line,
251
+ impact: 'Hard to maintain and may indicate performance issues',
252
+ suggestion: 'Refactor into smaller, more focused functions',
253
+ });
254
+ }
255
+ }
256
+ }
257
+ // Check for nested loops
258
+ const nestedLoopRegex = /for\s*\([^)]+\)[^{]*\{[^}]*for\s*\([^)]+\)/gs;
259
+ if (nestedLoopRegex.test(content)) {
260
+ issues.push({
261
+ type: 'major',
262
+ category: 'computation',
263
+ title: 'Nested loops detected',
264
+ description: 'Nested loops can cause O(n²) or worse complexity',
265
+ file,
266
+ impact: 'Performance degrades rapidly with data size',
267
+ suggestion: 'Consider using Map/Set for lookups or restructuring algorithm',
268
+ });
269
+ }
270
+ // Check for synchronous blocking operations
271
+ const blockingPatterns = [
272
+ { pattern: /fs\.readFileSync/, name: 'Synchronous file read' },
273
+ { pattern: /fs\.writeFileSync/, name: 'Synchronous file write' },
274
+ { pattern: /JSON\.parse\s*\([^)]*readFileSync/, name: 'Sync file read + JSON parse' },
275
+ ];
276
+ for (const { pattern, name } of blockingPatterns) {
277
+ if (pattern.test(content)) {
278
+ issues.push({
279
+ type: 'major',
280
+ category: 'computation',
281
+ title: name,
282
+ description: 'Synchronous operation blocks the event loop',
283
+ file,
284
+ impact: 'UI freezing and poor responsiveness',
285
+ suggestion: 'Use async alternatives (readFile, writeFile)',
286
+ });
287
+ }
288
+ }
289
+ // Check for expensive regex in loops
290
+ const regexInLoopRegex = /(?:for|while)\s*\([^)]+\)[^{]*\{[^}]*(?:\.match|\.replace|\.test)\s*\(/gs;
291
+ if (regexInLoopRegex.test(content)) {
292
+ optimizationOpportunities.push({
293
+ location: file,
294
+ type: 'regex',
295
+ suggestion: 'Move regex pattern outside loop to avoid recompilation',
296
+ });
297
+ }
298
+ // Check for unnecessary array spread
299
+ if (content.includes('[...') && content.includes('.push(')) {
300
+ optimizationOpportunities.push({
301
+ location: file,
302
+ type: 'array',
303
+ suggestion: 'Consider using push() instead of spread for building arrays',
304
+ });
305
+ }
306
+ }
307
+ catch {
308
+ // Skip files that can't be read
309
+ }
310
+ }
311
+ return { issues, heavyFunctions, optimizationOpportunities };
312
+ }
313
+ // ============================================
314
+ // Network Analysis
315
+ // ============================================
316
+ async function analyzeNetwork(files) {
317
+ const issues = [];
318
+ const apiCalls = [];
319
+ const suggestions = [];
320
+ for (const file of files) {
321
+ try {
322
+ const content = fs.readFileSync(file, 'utf-8');
323
+ // Find fetch/axios calls
324
+ const fetchRegex = /(?:fetch|axios\.(?:get|post|put|delete|patch))\s*\(\s*['"`]([^'"`]+)['"`]/g;
325
+ let match;
326
+ while ((match = fetchRegex.exec(content)) !== null) {
327
+ const endpoint = match[1] || '';
328
+ const line = getLineNumber(content, match.index);
329
+ // Check for error handling
330
+ const surroundingCode = content.substring(Math.max(0, match.index - 200), match.index + 500);
331
+ const hasErrorHandling = surroundingCode.includes('.catch') || surroundingCode.includes('try');
332
+ const hasRetry = surroundingCode.includes('retry') || surroundingCode.includes('Retry');
333
+ const hasCaching = surroundingCode.includes('cache') || surroundingCode.includes('Cache');
334
+ apiCalls.push({
335
+ endpoint,
336
+ file,
337
+ line,
338
+ hasErrorHandling,
339
+ hasRetry,
340
+ hasCaching,
341
+ });
342
+ if (!hasErrorHandling) {
343
+ issues.push({
344
+ type: 'major',
345
+ category: 'network',
346
+ title: 'API call without error handling',
347
+ description: `fetch/axios call to ${endpoint} has no error handling`,
348
+ file,
349
+ line,
350
+ impact: 'Unhandled network errors may crash the app',
351
+ suggestion: 'Add .catch() or try/catch for error handling',
352
+ });
353
+ }
354
+ }
355
+ // Check for API calls in useEffect without abort controller
356
+ if (content.includes('useEffect') && (content.includes('fetch') || content.includes('axios'))) {
357
+ if (!content.includes('AbortController') && !content.includes('abortController')) {
358
+ issues.push({
359
+ type: 'minor',
360
+ category: 'network',
361
+ title: 'API call in useEffect without AbortController',
362
+ description: 'API calls may complete after component unmounts',
363
+ file,
364
+ impact: 'Memory leaks and state updates on unmounted components',
365
+ suggestion: 'Use AbortController to cancel requests on unmount',
366
+ });
367
+ }
368
+ }
369
+ // Check for waterfall requests
370
+ const awaitFetchCount = (content.match(/await\s+(?:fetch|axios)/g) || []).length;
371
+ if (awaitFetchCount > 2) {
372
+ const hasPromiseAll = content.includes('Promise.all');
373
+ if (!hasPromiseAll) {
374
+ issues.push({
375
+ type: 'major',
376
+ category: 'network',
377
+ title: 'Sequential API calls detected',
378
+ description: `${awaitFetchCount} sequential await calls may cause waterfall requests`,
379
+ file,
380
+ impact: 'Increased loading time due to sequential requests',
381
+ suggestion: 'Use Promise.all() for independent requests',
382
+ estimatedGain: 'Reduce loading time by parallelizing requests',
383
+ });
384
+ }
385
+ }
386
+ }
387
+ catch {
388
+ // Skip files that can't be read
389
+ }
390
+ }
391
+ // Generate suggestions based on findings
392
+ if (apiCalls.some(a => !a.hasCaching)) {
393
+ suggestions.push('Consider implementing response caching for frequently called endpoints');
394
+ }
395
+ if (apiCalls.some(a => !a.hasRetry)) {
396
+ suggestions.push('Add retry logic for critical API endpoints');
397
+ }
398
+ if (apiCalls.length > 10) {
399
+ suggestions.push('Consider implementing request batching to reduce network overhead');
400
+ }
401
+ return { issues, apiCalls, suggestions };
402
+ }
403
+ // ============================================
404
+ // Bundle Analysis
405
+ // ============================================
406
+ async function analyzeBundle(projectPath, files, platform) {
407
+ const issues = [];
408
+ const largeImports = [];
409
+ const unusedExports = [];
410
+ const treeShakingOpportunities = [];
411
+ // Known large libraries
412
+ const largeLibraries = {
413
+ 'moment': { size: '~300KB', alternative: 'date-fns or dayjs' },
414
+ 'lodash': { size: '~70KB', alternative: 'lodash-es with tree-shaking' },
415
+ 'rxjs': { size: '~50KB', alternative: 'Import only needed operators' },
416
+ 'chart.js': { size: '~200KB', alternative: 'react-chartjs-2 with lazy loading' },
417
+ 'd3': { size: '~250KB', alternative: 'Import only needed d3 modules' },
418
+ 'firebase': { size: '~300KB', alternative: 'Use modular Firebase SDK' },
419
+ '@material-ui': { size: '~300KB', alternative: 'Import components individually' },
420
+ 'antd': { size: '~500KB', alternative: 'Use babel-plugin-import' },
421
+ };
422
+ const allImports = new Map();
423
+ const allExports = new Map();
424
+ for (const file of files) {
425
+ try {
426
+ const content = fs.readFileSync(file, 'utf-8');
427
+ // Check for large library imports
428
+ const importRegex = /import\s+(?:\*\s+as\s+\w+|\w+|\{[^}]+\})\s+from\s+['"]([^'"]+)['"]/g;
429
+ let match;
430
+ while ((match = importRegex.exec(content)) !== null) {
431
+ const module = match[1] || '';
432
+ const baseName = module.split('/')[0] || module;
433
+ // Track imports
434
+ if (!allImports.has(baseName)) {
435
+ allImports.set(baseName, []);
436
+ }
437
+ allImports.get(baseName).push(file);
438
+ // Check for known large libraries
439
+ if (largeLibraries[baseName]) {
440
+ const lib = largeLibraries[baseName];
441
+ if (!largeImports.some(i => i.module === baseName)) {
442
+ largeImports.push({
443
+ module: baseName,
444
+ file,
445
+ estimatedSize: lib?.size,
446
+ alternative: lib?.alternative,
447
+ });
448
+ issues.push({
449
+ type: 'minor',
450
+ category: 'bundle',
451
+ title: `Large library import: ${baseName}`,
452
+ description: `${baseName} adds approximately ${lib?.size} to bundle`,
453
+ file,
454
+ impact: 'Increased bundle size and load time',
455
+ suggestion: lib?.alternative || 'Consider alternatives or lazy loading',
456
+ });
457
+ }
458
+ }
459
+ // Check for full library imports that should be tree-shaken
460
+ if (content.includes(`import ${baseName} from`) || content.includes(`import * as`)) {
461
+ if (['lodash', 'rxjs', 'd3', 'ramda'].includes(baseName)) {
462
+ treeShakingOpportunities.push(`${baseName} in ${file}: Import only used functions instead of entire library`);
463
+ }
464
+ }
465
+ }
466
+ // Track exports
467
+ const exportRegex = /export\s+(?:const|let|var|function|class)\s+(\w+)/g;
468
+ while ((match = exportRegex.exec(content)) !== null) {
469
+ const name = match[1] || '';
470
+ if (!allExports.has(file)) {
471
+ allExports.set(file, []);
472
+ }
473
+ allExports.get(file).push(name);
474
+ }
475
+ }
476
+ catch {
477
+ // Skip files that can't be read
478
+ }
479
+ }
480
+ // Check for unused exports (basic check)
481
+ for (const [exportFile, exports] of allExports.entries()) {
482
+ for (const exp of exports) {
483
+ let isUsed = false;
484
+ for (const file of files) {
485
+ if (file === exportFile)
486
+ continue;
487
+ try {
488
+ const content = fs.readFileSync(file, 'utf-8');
489
+ if (content.includes(exp)) {
490
+ isUsed = true;
491
+ break;
492
+ }
493
+ }
494
+ catch {
495
+ // Skip
496
+ }
497
+ }
498
+ if (!isUsed && exp !== 'default') {
499
+ unusedExports.push(`${exp} in ${exportFile}`);
500
+ }
501
+ }
502
+ }
503
+ // Check package.json for bundle-affecting issues
504
+ try {
505
+ const packageJsonPath = path.join(projectPath, 'package.json');
506
+ if (fs.existsSync(packageJsonPath)) {
507
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
508
+ // Check for dev dependencies that might be bundled
509
+ const devDeps = Object.keys(packageJson.devDependencies || {});
510
+ const prodDeps = Object.keys(packageJson.dependencies || {});
511
+ for (const [module, importFiles] of allImports.entries()) {
512
+ if (devDeps.includes(module) && !prodDeps.includes(module)) {
513
+ issues.push({
514
+ type: 'minor',
515
+ category: 'bundle',
516
+ title: `Dev dependency imported in production code`,
517
+ description: `${module} is a devDependency but imported in ${importFiles.join(', ')}`,
518
+ file: importFiles[0] || '',
519
+ impact: 'May cause build issues or unnecessary bundle size',
520
+ suggestion: 'Move to dependencies or remove import',
521
+ });
522
+ }
523
+ }
524
+ }
525
+ }
526
+ catch {
527
+ // Skip package.json analysis
528
+ }
529
+ return { issues, largeImports, unusedExports, treeShakingOpportunities };
530
+ }
531
+ // ============================================
532
+ // Recommendations Generator
533
+ // ============================================
534
+ function generateRecommendations(rendering, computation, network, bundle) {
535
+ const recommendations = [];
536
+ // Rendering recommendations
537
+ if (rendering.memoizationOpportunities.length > 0) {
538
+ recommendations.push({
539
+ priority: 'high',
540
+ category: 'Rendering',
541
+ description: `Add React.memo to ${rendering.memoizationOpportunities.length} components to prevent unnecessary re-renders`,
542
+ estimatedImpact: '10-50% render performance improvement',
543
+ });
544
+ }
545
+ if (rendering.issues.filter(i => i.title.includes('inline')).length > 3) {
546
+ recommendations.push({
547
+ priority: 'medium',
548
+ category: 'Rendering',
549
+ description: 'Refactor inline functions and objects to prevent re-creation on each render',
550
+ estimatedImpact: 'Reduced garbage collection and smoother UI',
551
+ });
552
+ }
553
+ // Computation recommendations
554
+ if (computation.heavyFunctions.length > 0) {
555
+ recommendations.push({
556
+ priority: 'medium',
557
+ category: 'Code Quality',
558
+ description: `Refactor ${computation.heavyFunctions.length} high-complexity functions for better maintainability`,
559
+ estimatedImpact: 'Improved code quality and potential performance gains',
560
+ });
561
+ }
562
+ // Network recommendations
563
+ if (network.issues.filter(i => i.title.includes('Sequential')).length > 0) {
564
+ recommendations.push({
565
+ priority: 'high',
566
+ category: 'Network',
567
+ description: 'Parallelize sequential API calls using Promise.all',
568
+ estimatedImpact: 'Significant reduction in loading time',
569
+ });
570
+ }
571
+ if (network.apiCalls.filter(a => !a.hasErrorHandling).length > 0) {
572
+ recommendations.push({
573
+ priority: 'high',
574
+ category: 'Network',
575
+ description: 'Add error handling to all API calls',
576
+ estimatedImpact: 'Improved app stability and user experience',
577
+ });
578
+ }
579
+ // Bundle recommendations
580
+ if (bundle.largeImports.length > 0) {
581
+ recommendations.push({
582
+ priority: 'medium',
583
+ category: 'Bundle Size',
584
+ description: `Optimize ${bundle.largeImports.length} large library imports using tree-shaking or alternatives`,
585
+ estimatedImpact: 'Reduced bundle size and faster load times',
586
+ });
587
+ }
588
+ if (bundle.treeShakingOpportunities.length > 0) {
589
+ recommendations.push({
590
+ priority: 'low',
591
+ category: 'Bundle Size',
592
+ description: 'Enable tree-shaking for lodash, rxjs, and other libraries',
593
+ estimatedImpact: '10-30% bundle size reduction',
594
+ });
595
+ }
596
+ return recommendations;
597
+ }
598
+ // ============================================
599
+ // Helper Functions
600
+ // ============================================
601
+ function getExtensionsForPlatform(platform) {
602
+ switch (platform) {
603
+ case 'ios':
604
+ return ['.swift', '.m', '.mm'];
605
+ case 'android':
606
+ return ['.kt', '.java'];
607
+ case 'flutter':
608
+ return ['.dart'];
609
+ case 'react-native':
610
+ case 'web':
611
+ default:
612
+ return ['.ts', '.tsx', '.js', '.jsx'];
613
+ }
614
+ }
615
+ function getAllSourceFiles(dir, extensions) {
616
+ const results = [];
617
+ const skipDirs = ['node_modules', '.git', 'build', 'dist', 'Pods', '.gradle', 'DerivedData', '__tests__', 'test'];
618
+ function walk(currentDir) {
619
+ try {
620
+ const items = fs.readdirSync(currentDir);
621
+ for (const item of items) {
622
+ if (skipDirs.includes(item))
623
+ continue;
624
+ const fullPath = path.join(currentDir, item);
625
+ const stat = fs.statSync(fullPath);
626
+ if (stat.isDirectory()) {
627
+ walk(fullPath);
628
+ }
629
+ else if (extensions.some(ext => item.endsWith(ext))) {
630
+ results.push(fullPath);
631
+ }
632
+ }
633
+ }
634
+ catch {
635
+ // Ignore errors
636
+ }
637
+ }
638
+ walk(dir);
639
+ return results;
640
+ }
641
+ function getLineNumber(content, index) {
642
+ return content.substring(0, index).split('\n').length;
643
+ }
644
+ exports.default = {
645
+ analyzePerformance,
646
+ };
647
+ //# sourceMappingURL=performanceAnalyzer.js.map