atlas-pipeline-mcp 1.0.23 → 1.0.26

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 (66) hide show
  1. package/README.md +117 -4
  2. package/dist/common/error-handling.d.ts +86 -0
  3. package/dist/common/error-handling.d.ts.map +1 -0
  4. package/dist/common/error-handling.js +226 -0
  5. package/dist/common/error-handling.js.map +1 -0
  6. package/dist/mcp.js +232 -0
  7. package/dist/mcp.js.map +1 -1
  8. package/dist/tools/animation-studio.d.ts +83 -0
  9. package/dist/tools/animation-studio.d.ts.map +1 -0
  10. package/dist/tools/animation-studio.js +1064 -0
  11. package/dist/tools/animation-studio.js.map +1 -0
  12. package/dist/tools/api-design-consultant.d.ts +92 -0
  13. package/dist/tools/api-design-consultant.d.ts.map +1 -0
  14. package/dist/tools/api-design-consultant.js +374 -0
  15. package/dist/tools/api-design-consultant.js.map +1 -0
  16. package/dist/tools/api-integration-helper.d.ts +141 -0
  17. package/dist/tools/api-integration-helper.d.ts.map +1 -0
  18. package/dist/tools/api-integration-helper.js +907 -0
  19. package/dist/tools/api-integration-helper.js.map +1 -0
  20. package/dist/tools/css-architecture-wizard.d.ts +86 -0
  21. package/dist/tools/css-architecture-wizard.d.ts.map +1 -0
  22. package/dist/tools/css-architecture-wizard.js +790 -0
  23. package/dist/tools/css-architecture-wizard.js.map +1 -0
  24. package/dist/tools/debug/error-classifier.d.ts +14 -0
  25. package/dist/tools/debug/error-classifier.d.ts.map +1 -0
  26. package/dist/tools/debug/error-classifier.js +40 -0
  27. package/dist/tools/debug/error-classifier.js.map +1 -0
  28. package/dist/tools/debug/language-detector.d.ts +16 -0
  29. package/dist/tools/debug/language-detector.d.ts.map +1 -0
  30. package/dist/tools/debug/language-detector.js +67 -0
  31. package/dist/tools/debug/language-detector.js.map +1 -0
  32. package/dist/tools/debug/stack-parser.d.ts +25 -0
  33. package/dist/tools/debug/stack-parser.d.ts.map +1 -0
  34. package/dist/tools/debug/stack-parser.js +122 -0
  35. package/dist/tools/debug/stack-parser.js.map +1 -0
  36. package/dist/tools/dependencies.d.ts.map +1 -1
  37. package/dist/tools/dependencies.js +50 -25
  38. package/dist/tools/dependencies.js.map +1 -1
  39. package/dist/tools/frontend-performance-doctor.d.ts +108 -0
  40. package/dist/tools/frontend-performance-doctor.d.ts.map +1 -0
  41. package/dist/tools/frontend-performance-doctor.js +731 -0
  42. package/dist/tools/frontend-performance-doctor.js.map +1 -0
  43. package/dist/tools/performance-optimizer.d.ts +97 -0
  44. package/dist/tools/performance-optimizer.d.ts.map +1 -0
  45. package/dist/tools/performance-optimizer.js +295 -0
  46. package/dist/tools/performance-optimizer.js.map +1 -0
  47. package/dist/tools/security-scanner.d.ts +74 -0
  48. package/dist/tools/security-scanner.d.ts.map +1 -0
  49. package/dist/tools/security-scanner.js +290 -0
  50. package/dist/tools/security-scanner.js.map +1 -0
  51. package/dist/tools/senior-mentor.d.ts +81 -0
  52. package/dist/tools/senior-mentor.d.ts.map +1 -0
  53. package/dist/tools/senior-mentor.js +308 -0
  54. package/dist/tools/senior-mentor.js.map +1 -0
  55. package/dist/tools/state-management-architect.d.ts +77 -0
  56. package/dist/tools/state-management-architect.d.ts.map +1 -0
  57. package/dist/tools/state-management-architect.js +323 -0
  58. package/dist/tools/state-management-architect.js.map +1 -0
  59. package/dist/tools/test-utils.d.ts.map +1 -1
  60. package/dist/tools/test-utils.js +109 -56
  61. package/dist/tools/test-utils.js.map +1 -1
  62. package/dist/tools/ui-ux-designer.d.ts +91 -0
  63. package/dist/tools/ui-ux-designer.d.ts.map +1 -0
  64. package/dist/tools/ui-ux-designer.js +907 -0
  65. package/dist/tools/ui-ux-designer.js.map +1 -0
  66. package/package.json +3 -2
@@ -0,0 +1,731 @@
1
+ /**
2
+ * Frontend Performance Doctor
3
+ *
4
+ * Analyzes frontend code and provides specific fixes for performance issues:
5
+ * - React/Vue re-render detection and fixes
6
+ * - Bundle size optimization recommendations
7
+ * - Lazy loading opportunities
8
+ * - Image optimization strategies
9
+ * - Memory leak detection
10
+ * - Core Web Vitals improvement suggestions
11
+ */
12
+ import { z } from 'zod';
13
+ import { getActiveProvider } from '../providers/index.js';
14
+ import { logger } from '../utils.js';
15
+ // ============================================================================
16
+ // Validation Schema
17
+ // ============================================================================
18
+ export const PerformanceRequestSchema = z.object({
19
+ code: z.string().min(1, 'Code is required'),
20
+ framework: z.enum(['react', 'vue', 'angular', 'svelte', 'next', 'nuxt']),
21
+ analysisType: z.enum(['full', 'render', 'bundle', 'network', 'quick']).optional().default('full'),
22
+ includeFixedCode: z.boolean().optional().default(true),
23
+ targetMetrics: z.array(z.string()).optional()
24
+ });
25
+ // ============================================================================
26
+ // Performance Patterns Database
27
+ // ============================================================================
28
+ const PERFORMANCE_PATTERNS = {
29
+ react: {
30
+ unnecessaryRerenders: {
31
+ patterns: [
32
+ /onClick=\{[^}]*\(\)\s*=>/g, // Inline arrow functions
33
+ /style=\{\{[^}]+\}\}/g, // Inline style objects
34
+ /\[\s*\.\.\.\w+\s*\]/g, // Spread in render
35
+ ],
36
+ fix: 'Use useCallback, useMemo, or extract to component level'
37
+ },
38
+ missingMemoization: {
39
+ patterns: [
40
+ /const\s+\w+\s*=\s*\w+\.filter\(/g,
41
+ /const\s+\w+\s*=\s*\w+\.map\(/g,
42
+ /const\s+\w+\s*=\s*\w+\.reduce\(/g,
43
+ ],
44
+ fix: 'Wrap expensive computations in useMemo'
45
+ },
46
+ missingLazyLoading: {
47
+ patterns: [
48
+ /import\s+\w+\s+from\s+['"][^'"]+['"];?\s*\/\/\s*large/gi,
49
+ /import\s+{\s*\w+\s*}\s+from\s+['"]@mui/g,
50
+ /import\s+\w+\s+from\s+['"]lodash['"];?/g,
51
+ ],
52
+ fix: 'Use React.lazy() and dynamic imports'
53
+ }
54
+ },
55
+ vue: {
56
+ unnecessaryReactivity: {
57
+ patterns: [
58
+ /ref\([^)]*\)/g,
59
+ /reactive\([^)]*\)/g,
60
+ ],
61
+ fix: 'Use shallowRef or shallowReactive for large objects'
62
+ },
63
+ missingComputed: {
64
+ patterns: [
65
+ /\.value\.filter\(/g,
66
+ /\.value\.map\(/g,
67
+ ],
68
+ fix: 'Use computed() for derived state'
69
+ }
70
+ }
71
+ };
72
+ const BUNDLE_OPTIMIZATIONS = {
73
+ 'lodash': {
74
+ issue: 'Full lodash import adds ~70KB',
75
+ fix: "import debounce from 'lodash/debounce'",
76
+ savings: '~65KB'
77
+ },
78
+ 'moment': {
79
+ issue: 'Moment.js is heavy (~300KB with locales)',
80
+ fix: "Replace with dayjs or date-fns",
81
+ savings: '~280KB'
82
+ },
83
+ '@mui/material': {
84
+ issue: 'Full MUI import prevents tree-shaking',
85
+ fix: "import Button from '@mui/material/Button'",
86
+ savings: '~100KB+'
87
+ },
88
+ 'antd': {
89
+ issue: 'Full Ant Design import is heavy',
90
+ fix: "Use babel-plugin-import for on-demand loading",
91
+ savings: '~200KB+'
92
+ }
93
+ };
94
+ // ============================================================================
95
+ // Main Functions
96
+ // ============================================================================
97
+ export async function analyzePerformance(request) {
98
+ const validated = PerformanceRequestSchema.parse(request);
99
+ logger.info(`Analyzing performance for ${validated.framework} code`);
100
+ const issues = [];
101
+ let issueId = 1;
102
+ // Analyze re-render issues
103
+ const renderIssues = analyzeRenderPerformance(validated.code, validated.framework, issueId);
104
+ issues.push(...renderIssues);
105
+ issueId += renderIssues.length;
106
+ // Analyze bundle size
107
+ const bundleIssues = analyzeBundleSize(validated.code, issueId);
108
+ issues.push(...bundleIssues);
109
+ issueId += bundleIssues.length;
110
+ // Analyze network patterns
111
+ const networkIssues = analyzeNetworkPatterns(validated.code, issueId);
112
+ issues.push(...networkIssues);
113
+ issueId += networkIssues.length;
114
+ // Analyze memory patterns
115
+ const memoryIssues = analyzeMemoryPatterns(validated.code, validated.framework, issueId);
116
+ issues.push(...memoryIssues);
117
+ issueId += memoryIssues.length;
118
+ // Analyze images
119
+ const imageIssues = analyzeImageUsage(validated.code, issueId);
120
+ issues.push(...imageIssues);
121
+ // Try to get AI-powered deep analysis
122
+ const provider = await getActiveProvider();
123
+ if (provider) {
124
+ const aiAnalysis = await getAIPerformanceAnalysis(provider, validated);
125
+ if (aiAnalysis.additionalIssues) {
126
+ issues.push(...aiAnalysis.additionalIssues);
127
+ }
128
+ }
129
+ // Generate bundle analysis
130
+ const bundleAnalysis = generateBundleAnalysis(validated.code);
131
+ // Generate Core Web Vitals analysis
132
+ const coreWebVitals = analyzeCoreWebVitals(validated.code, validated.framework);
133
+ // Calculate score
134
+ const criticalCount = issues.filter(i => i.severity === 'critical').length;
135
+ const highCount = issues.filter(i => i.severity === 'high').length;
136
+ const mediumCount = issues.filter(i => i.severity === 'medium').length;
137
+ let score = 100;
138
+ score -= criticalCount * 20;
139
+ score -= highCount * 10;
140
+ score -= mediumCount * 5;
141
+ score = Math.max(0, score);
142
+ const grade = score >= 90 ? 'A' : score >= 80 ? 'B' : score >= 70 ? 'C' : score >= 60 ? 'D' : 'F';
143
+ // Generate optimized code snippets
144
+ const generatedOptimizations = validated.includeFixedCode
145
+ ? generateOptimizedCode(validated.code, validated.framework, issues)
146
+ : {};
147
+ // Generate quick wins and long-term recommendations
148
+ const quickWins = generateQuickWins(issues);
149
+ const longTermOptimizations = generateLongTermOptimizations(issues, validated.framework);
150
+ return {
151
+ summary: {
152
+ score,
153
+ grade,
154
+ criticalIssues: criticalCount,
155
+ totalIssues: issues.length
156
+ },
157
+ issues,
158
+ bundleAnalysis,
159
+ coreWebVitals,
160
+ quickWins,
161
+ longTermOptimizations,
162
+ generatedOptimizations
163
+ };
164
+ }
165
+ // ============================================================================
166
+ // Analysis Functions
167
+ // ============================================================================
168
+ function analyzeRenderPerformance(code, framework, startId) {
169
+ const issues = [];
170
+ let id = startId;
171
+ if (framework === 'react' || framework === 'next') {
172
+ // Check for inline arrow functions in JSX
173
+ const inlineArrowRegex = /onClick=\{(?:\([^)]*\))?\s*=>\s*[^}]+\}/g;
174
+ const inlineMatches = code.match(inlineArrowRegex);
175
+ if (inlineMatches && inlineMatches.length > 2) {
176
+ issues.push({
177
+ id: `PERF-${id++}`,
178
+ severity: 'high',
179
+ category: 'render',
180
+ title: 'Inline Arrow Functions in JSX',
181
+ description: `Found ${inlineMatches.length} inline arrow functions in event handlers. These create new function references on every render.`,
182
+ impact: 'Causes unnecessary re-renders of child components, especially with React.memo',
183
+ currentCode: inlineMatches[0],
184
+ fixedCode: `// Before: onClick={() => handleClick(id)}
185
+ // After:
186
+ const handleClickMemo = useCallback((id) => {
187
+ // handler logic
188
+ }, [/* dependencies */]);
189
+
190
+ // In JSX:
191
+ onClick={handleClickMemo}`,
192
+ estimatedImprovement: '10-30% fewer re-renders'
193
+ });
194
+ }
195
+ // Check for inline style objects
196
+ const inlineStyleRegex = /style=\{\{[^}]+\}\}/g;
197
+ const styleMatches = code.match(inlineStyleRegex);
198
+ if (styleMatches && styleMatches.length > 3) {
199
+ issues.push({
200
+ id: `PERF-${id++}`,
201
+ severity: 'medium',
202
+ category: 'render',
203
+ title: 'Inline Style Objects',
204
+ description: `Found ${styleMatches.length} inline style objects. Each creates a new object on every render.`,
205
+ impact: 'Breaks memoization and causes unnecessary re-renders',
206
+ currentCode: styleMatches[0],
207
+ fixedCode: `// Before: style={{ marginTop: 10, color: 'red' }}
208
+ // After:
209
+ const styles = useMemo(() => ({
210
+ marginTop: 10,
211
+ color: 'red'
212
+ }), []);
213
+ // Or use CSS classes/CSS-in-JS`,
214
+ estimatedImprovement: '5-15% fewer re-renders'
215
+ });
216
+ }
217
+ // Check for missing React.memo on components
218
+ if (!code.includes('React.memo') && !code.includes('memo(') && code.includes('export')) {
219
+ const hasProps = /function\s+\w+\s*\(\s*\{\s*\w+/g.test(code) || /const\s+\w+\s*=\s*\(\s*\{\s*\w+/g.test(code);
220
+ if (hasProps) {
221
+ issues.push({
222
+ id: `PERF-${id++}`,
223
+ severity: 'medium',
224
+ category: 'render',
225
+ title: 'Component Not Memoized',
226
+ description: 'Component receives props but is not wrapped in React.memo',
227
+ impact: 'Component re-renders whenever parent re-renders, even if props unchanged',
228
+ fixedCode: `// Wrap component export:
229
+ export default React.memo(YourComponent);
230
+
231
+ // Or with comparison function:
232
+ export default React.memo(YourComponent, (prevProps, nextProps) => {
233
+ return prevProps.id === nextProps.id;
234
+ });`,
235
+ estimatedImprovement: '20-50% fewer re-renders'
236
+ });
237
+ }
238
+ }
239
+ // Check for expensive computations in render
240
+ const expensiveOps = code.match(/(?:\.filter\(|\.map\(|\.reduce\(|\.sort\(|\.find\()[^)]+\)/g);
241
+ if (expensiveOps && expensiveOps.length > 0) {
242
+ const notMemoized = !code.includes('useMemo');
243
+ if (notMemoized) {
244
+ issues.push({
245
+ id: `PERF-${id++}`,
246
+ severity: 'high',
247
+ category: 'render',
248
+ title: 'Expensive Computation in Render',
249
+ description: `Found ${expensiveOps.length} array operations that run on every render`,
250
+ impact: 'Blocks main thread and slows down UI responsiveness',
251
+ currentCode: expensiveOps[0],
252
+ fixedCode: `// Wrap in useMemo:
253
+ const filteredItems = useMemo(() => {
254
+ return items.filter(item => item.active);
255
+ }, [items]);
256
+
257
+ const sortedItems = useMemo(() => {
258
+ return [...items].sort((a, b) => a.name.localeCompare(b.name));
259
+ }, [items]);`,
260
+ estimatedImprovement: '30-70% faster renders'
261
+ });
262
+ }
263
+ }
264
+ }
265
+ if (framework === 'vue' || framework === 'nuxt') {
266
+ // Check for missing computed properties
267
+ if (code.includes('.value.filter(') || code.includes('.value.map(')) {
268
+ if (!code.includes('computed(')) {
269
+ issues.push({
270
+ id: `PERF-${id++}`,
271
+ severity: 'high',
272
+ category: 'render',
273
+ title: 'Missing Computed Properties',
274
+ description: 'Derived state calculated directly from reactive refs',
275
+ impact: 'Recalculates on every access instead of caching',
276
+ fixedCode: `// Before: items.value.filter(i => i.active)
277
+ // After:
278
+ const activeItems = computed(() => {
279
+ return items.value.filter(i => i.active);
280
+ });`,
281
+ estimatedImprovement: '40-60% fewer calculations'
282
+ });
283
+ }
284
+ }
285
+ // Check for v-if with v-for
286
+ if (code.includes('v-for') && code.includes('v-if')) {
287
+ issues.push({
288
+ id: `PERF-${id++}`,
289
+ severity: 'medium',
290
+ category: 'render',
291
+ title: 'v-if with v-for Anti-pattern',
292
+ description: 'Using v-if and v-for on the same element',
293
+ impact: 'v-for has higher priority, causing unnecessary iterations',
294
+ fixedCode: `<!-- Before: <li v-for="item in items" v-if="item.active"> -->
295
+ <!-- After: Use computed or wrap in template -->
296
+ <template v-for="item in items" :key="item.id">
297
+ <li v-if="item.active">{{ item.name }}</li>
298
+ </template>
299
+
300
+ <!-- Or better, use computed: -->
301
+ const activeItems = computed(() => items.filter(i => i.active));`,
302
+ estimatedImprovement: '20-40% faster list renders'
303
+ });
304
+ }
305
+ }
306
+ return issues;
307
+ }
308
+ function analyzeBundleSize(code, startId) {
309
+ const issues = [];
310
+ let id = startId;
311
+ for (const [pkg, info] of Object.entries(BUNDLE_OPTIMIZATIONS)) {
312
+ if (code.includes(`from '${pkg}'`) || code.includes(`from "${pkg}"`)) {
313
+ // Check if it's a barrel import
314
+ const barrelImport = new RegExp(`import\\s+{[^}]+}\\s+from\\s+['"]${pkg}['"]`);
315
+ const defaultImport = new RegExp(`import\\s+\\w+\\s+from\\s+['"]${pkg}['"]`);
316
+ if (barrelImport.test(code) || defaultImport.test(code)) {
317
+ issues.push({
318
+ id: `PERF-${id++}`,
319
+ severity: 'high',
320
+ category: 'bundle',
321
+ title: `Heavy Import: ${pkg}`,
322
+ description: info.issue,
323
+ impact: `Adding unnecessary ${info.savings} to your bundle`,
324
+ fixedCode: info.fix,
325
+ estimatedImprovement: `Save ${info.savings}`
326
+ });
327
+ }
328
+ }
329
+ }
330
+ // Check for missing dynamic imports
331
+ const heavyImports = code.match(/import\s+\w+\s+from\s+['"][^'"]+['"]/g) || [];
332
+ if (heavyImports.length > 10) {
333
+ issues.push({
334
+ id: `PERF-${id++}`,
335
+ severity: 'medium',
336
+ category: 'bundle',
337
+ title: 'Many Static Imports',
338
+ description: `${heavyImports.length} static imports found. Consider code-splitting.`,
339
+ impact: 'Large initial bundle size, slower page load',
340
+ fixedCode: `// Convert static imports to dynamic:
341
+ // Before: import HeavyComponent from './HeavyComponent';
342
+
343
+ // After (React):
344
+ const HeavyComponent = React.lazy(() => import('./HeavyComponent'));
345
+
346
+ // After (Vue):
347
+ const HeavyComponent = defineAsyncComponent(() =>
348
+ import('./HeavyComponent.vue')
349
+ );
350
+
351
+ // After (Next.js):
352
+ import dynamic from 'next/dynamic';
353
+ const HeavyComponent = dynamic(() => import('./HeavyComponent'));`,
354
+ estimatedImprovement: '30-50% smaller initial bundle'
355
+ });
356
+ }
357
+ return issues;
358
+ }
359
+ function analyzeNetworkPatterns(code, startId) {
360
+ const issues = [];
361
+ let id = startId;
362
+ // Check for fetch without caching
363
+ if (code.includes('fetch(') && !code.includes('cache')) {
364
+ issues.push({
365
+ id: `PERF-${id++}`,
366
+ severity: 'medium',
367
+ category: 'network',
368
+ title: 'Fetch Without Caching Strategy',
369
+ description: 'API calls made without caching configuration',
370
+ impact: 'Redundant network requests, slower perceived performance',
371
+ fixedCode: `// Add caching with React Query or SWR:
372
+ import { useQuery } from '@tanstack/react-query';
373
+
374
+ const { data, isLoading } = useQuery({
375
+ queryKey: ['users'],
376
+ queryFn: () => fetch('/api/users').then(r => r.json()),
377
+ staleTime: 5 * 60 * 1000, // 5 minutes
378
+ cacheTime: 30 * 60 * 1000, // 30 minutes
379
+ });
380
+
381
+ // Or with SWR:
382
+ import useSWR from 'swr';
383
+ const { data } = useSWR('/api/users', fetcher, {
384
+ revalidateOnFocus: false,
385
+ dedupingInterval: 60000,
386
+ });`,
387
+ estimatedImprovement: '50-80% fewer API calls'
388
+ });
389
+ }
390
+ // Check for waterfall requests
391
+ const fetchCalls = (code.match(/await\s+fetch\(/g) || []).length;
392
+ if (fetchCalls > 2) {
393
+ issues.push({
394
+ id: `PERF-${id++}`,
395
+ severity: 'high',
396
+ category: 'network',
397
+ title: 'Sequential API Calls (Waterfall)',
398
+ description: `Found ${fetchCalls} sequential fetch calls`,
399
+ impact: 'Each request waits for previous to complete, multiplying latency',
400
+ fixedCode: `// Before (waterfall):
401
+ const users = await fetch('/api/users');
402
+ const posts = await fetch('/api/posts');
403
+ const comments = await fetch('/api/comments');
404
+
405
+ // After (parallel):
406
+ const [users, posts, comments] = await Promise.all([
407
+ fetch('/api/users'),
408
+ fetch('/api/posts'),
409
+ fetch('/api/comments'),
410
+ ]);`,
411
+ estimatedImprovement: '60-80% faster data loading'
412
+ });
413
+ }
414
+ return issues;
415
+ }
416
+ function analyzeMemoryPatterns(code, framework, startId) {
417
+ const issues = [];
418
+ let id = startId;
419
+ // Check for event listener cleanup
420
+ if (code.includes('addEventListener') && !code.includes('removeEventListener')) {
421
+ issues.push({
422
+ id: `PERF-${id++}`,
423
+ severity: 'critical',
424
+ category: 'memory',
425
+ title: 'Memory Leak: Event Listener Not Cleaned Up',
426
+ description: 'addEventListener used without corresponding removeEventListener',
427
+ impact: 'Memory grows over time, eventually causing crashes',
428
+ fixedCode: `// React:
429
+ useEffect(() => {
430
+ const handler = (e) => { /* ... */ };
431
+ window.addEventListener('resize', handler);
432
+
433
+ return () => {
434
+ window.removeEventListener('resize', handler);
435
+ };
436
+ }, []);
437
+
438
+ // Vue:
439
+ onMounted(() => {
440
+ window.addEventListener('resize', handler);
441
+ });
442
+ onUnmounted(() => {
443
+ window.removeEventListener('resize', handler);
444
+ });`,
445
+ estimatedImprovement: 'Prevents memory leaks'
446
+ });
447
+ }
448
+ // Check for setInterval cleanup
449
+ if (code.includes('setInterval') && !code.includes('clearInterval')) {
450
+ issues.push({
451
+ id: `PERF-${id++}`,
452
+ severity: 'critical',
453
+ category: 'memory',
454
+ title: 'Memory Leak: Interval Not Cleared',
455
+ description: 'setInterval used without clearInterval cleanup',
456
+ impact: 'Interval continues running after component unmount',
457
+ fixedCode: `// React:
458
+ useEffect(() => {
459
+ const intervalId = setInterval(() => {
460
+ // your logic
461
+ }, 1000);
462
+
463
+ return () => clearInterval(intervalId);
464
+ }, []);`,
465
+ estimatedImprovement: 'Prevents memory leaks'
466
+ });
467
+ }
468
+ // Check for subscription cleanup
469
+ if ((code.includes('.subscribe(') || code.includes('on(')) &&
470
+ !code.includes('unsubscribe') && !code.includes('.off(')) {
471
+ issues.push({
472
+ id: `PERF-${id++}`,
473
+ severity: 'high',
474
+ category: 'memory',
475
+ title: 'Subscription Not Cleaned Up',
476
+ description: 'Observable/event subscription without cleanup',
477
+ impact: 'Memory accumulates with each mount/unmount cycle',
478
+ fixedCode: `// React:
479
+ useEffect(() => {
480
+ const subscription = observable.subscribe(handler);
481
+ return () => subscription.unsubscribe();
482
+ }, []);`,
483
+ estimatedImprovement: 'Prevents memory leaks'
484
+ });
485
+ }
486
+ return issues;
487
+ }
488
+ function analyzeImageUsage(code, startId) {
489
+ const issues = [];
490
+ let id = startId;
491
+ // Check for images without optimization
492
+ const imgTags = code.match(/<img[^>]+>/g) || [];
493
+ for (const img of imgTags) {
494
+ if (!img.includes('loading=') && !img.includes('loading="lazy"')) {
495
+ issues.push({
496
+ id: `PERF-${id++}`,
497
+ severity: 'medium',
498
+ category: 'images',
499
+ title: 'Image Without Lazy Loading',
500
+ description: 'Images should use lazy loading for better initial load',
501
+ impact: 'All images load immediately, blocking page render',
502
+ currentCode: img,
503
+ fixedCode: `<!-- Add lazy loading -->
504
+ <img src="image.jpg" loading="lazy" alt="description" />
505
+
506
+ <!-- For Next.js, use Image component -->
507
+ import Image from 'next/image';
508
+ <Image src="/image.jpg" width={500} height={300} alt="description" />`,
509
+ estimatedImprovement: '20-40% faster initial load'
510
+ });
511
+ break; // Only report once
512
+ }
513
+ if (!img.includes('width=') || !img.includes('height=')) {
514
+ issues.push({
515
+ id: `PERF-${id++}`,
516
+ severity: 'high',
517
+ category: 'images',
518
+ title: 'Image Without Dimensions',
519
+ description: 'Images without width/height cause layout shifts',
520
+ impact: 'High CLS score, poor user experience',
521
+ fixedCode: `<!-- Always specify dimensions -->
522
+ <img src="image.jpg" width="500" height="300" alt="description" />`,
523
+ estimatedImprovement: 'Eliminates CLS from images'
524
+ });
525
+ break;
526
+ }
527
+ }
528
+ return issues;
529
+ }
530
+ function generateBundleAnalysis(code) {
531
+ const imports = code.match(/import\s+(?:{[^}]+}|\w+)\s+from\s+['"]([^'"]+)['"]/g) || [];
532
+ const largestModules = [];
533
+ // Detect heavy imports
534
+ const heavyPackages = {
535
+ 'lodash': { size: '~70KB', suggestion: 'Use lodash-es or individual imports' },
536
+ 'moment': { size: '~300KB', suggestion: 'Replace with dayjs (2KB)' },
537
+ '@mui/material': { size: '~200KB', suggestion: 'Use path imports' },
538
+ 'antd': { size: '~500KB', suggestion: 'Use babel-plugin-import' },
539
+ 'recharts': { size: '~150KB', suggestion: 'Lazy load chart components' },
540
+ 'three': { size: '~600KB', suggestion: 'Use dynamic import' },
541
+ 'd3': { size: '~250KB', suggestion: 'Import only needed modules' },
542
+ };
543
+ for (const [pkg, info] of Object.entries(heavyPackages)) {
544
+ if (code.includes(`'${pkg}'`) || code.includes(`"${pkg}"`)) {
545
+ largestModules.push({
546
+ name: pkg,
547
+ size: info.size,
548
+ suggestion: info.suggestion
549
+ });
550
+ }
551
+ }
552
+ return {
553
+ totalSize: 'Analysis requires build tools',
554
+ largestModules,
555
+ unusedExports: [],
556
+ duplicateDependencies: [],
557
+ treeshakingOpportunities: largestModules.map(m => m.name)
558
+ };
559
+ }
560
+ function analyzeCoreWebVitals(code, framework) {
561
+ const vitals = {
562
+ lcp: { score: 'Good', issues: [], fixes: [] },
563
+ fid: { score: 'Good', issues: [], fixes: [] },
564
+ cls: { score: 'Good', issues: [], fixes: [] },
565
+ ttfb: { score: 'Good', issues: [], fixes: [] }
566
+ };
567
+ // LCP Analysis
568
+ if (code.includes('<img') && !code.includes('priority') && !code.includes('fetchpriority')) {
569
+ vitals.lcp.score = 'Needs Improvement';
570
+ vitals.lcp.issues.push('Hero images not prioritized');
571
+ vitals.lcp.fixes.push('Add fetchpriority="high" to LCP image');
572
+ }
573
+ // CLS Analysis
574
+ if (code.includes('<img') && (!code.includes('width=') || !code.includes('height='))) {
575
+ vitals.cls.score = 'Poor';
576
+ vitals.cls.issues.push('Images without dimensions cause layout shifts');
577
+ vitals.cls.fixes.push('Add explicit width and height to all images');
578
+ }
579
+ // FID Analysis
580
+ const heavyOperations = (code.match(/\.map\(|\.filter\(|\.reduce\(|\.sort\(/g) || []).length;
581
+ if (heavyOperations > 5) {
582
+ vitals.fid.score = 'Needs Improvement';
583
+ vitals.fid.issues.push('Heavy JavaScript operations may block main thread');
584
+ vitals.fid.fixes.push('Use Web Workers for heavy computations');
585
+ }
586
+ return vitals;
587
+ }
588
+ function generateQuickWins(issues) {
589
+ const quickWins = [];
590
+ if (issues.some(i => i.category === 'images')) {
591
+ quickWins.push('Add loading="lazy" to all below-fold images');
592
+ }
593
+ if (issues.some(i => i.category === 'render' && i.title.includes('Inline'))) {
594
+ quickWins.push('Move inline functions to useCallback hooks');
595
+ }
596
+ if (issues.some(i => i.category === 'bundle')) {
597
+ quickWins.push('Replace heavy libraries with lightweight alternatives');
598
+ }
599
+ if (issues.some(i => i.category === 'network')) {
600
+ quickWins.push('Add caching layer with React Query or SWR');
601
+ }
602
+ if (quickWins.length === 0) {
603
+ quickWins.push('Code looks well optimized! Consider profiling with React DevTools');
604
+ }
605
+ return quickWins;
606
+ }
607
+ function generateLongTermOptimizations(issues, framework) {
608
+ const optimizations = [];
609
+ optimizations.push('Implement code-splitting with dynamic imports');
610
+ optimizations.push('Set up bundle analyzer to track size over time');
611
+ optimizations.push('Configure proper caching headers on your CDN');
612
+ if (framework === 'react' || framework === 'next') {
613
+ optimizations.push('Consider using React Server Components for static content');
614
+ optimizations.push('Implement streaming SSR for faster TTFB');
615
+ }
616
+ if (framework === 'vue' || framework === 'nuxt') {
617
+ optimizations.push('Use async components for route-level code splitting');
618
+ }
619
+ return optimizations;
620
+ }
621
+ function generateOptimizedCode(code, framework, issues) {
622
+ const optimizations = {};
623
+ // Generate lazy loading code
624
+ if (issues.some(i => i.category === 'bundle')) {
625
+ if (framework === 'react' || framework === 'next') {
626
+ optimizations.lazyLoadingCode = `// Lazy loading setup for React
627
+ import React, { Suspense, lazy } from 'react';
628
+
629
+ // Lazy load heavy components
630
+ const Dashboard = lazy(() => import('./Dashboard'));
631
+ const Charts = lazy(() => import('./Charts'));
632
+ const Settings = lazy(() => import('./Settings'));
633
+
634
+ // Loading fallback
635
+ const Loading = () => <div className="loading-spinner">Loading...</div>;
636
+
637
+ // Usage in your app
638
+ function App() {
639
+ return (
640
+ <Suspense fallback={<Loading />}>
641
+ <Routes>
642
+ <Route path="/dashboard" element={<Dashboard />} />
643
+ <Route path="/charts" element={<Charts />} />
644
+ <Route path="/settings" element={<Settings />} />
645
+ </Routes>
646
+ </Suspense>
647
+ );
648
+ }`;
649
+ }
650
+ }
651
+ // Generate memoization code
652
+ if (issues.some(i => i.category === 'render')) {
653
+ optimizations.memoizationCode = `// Memoization patterns
654
+ import { memo, useMemo, useCallback } from 'react';
655
+
656
+ // Memoize expensive computations
657
+ const ExpensiveComponent = memo(function ExpensiveComponent({ items, filter }) {
658
+ // Memoize filtered results
659
+ const filteredItems = useMemo(() => {
660
+ return items.filter(item => item.category === filter);
661
+ }, [items, filter]);
662
+
663
+ // Memoize callbacks
664
+ const handleClick = useCallback((id) => {
665
+ console.log('Clicked:', id);
666
+ }, []);
667
+
668
+ // Memoize style objects
669
+ const containerStyle = useMemo(() => ({
670
+ padding: 20,
671
+ backgroundColor: '#f5f5f5'
672
+ }), []);
673
+
674
+ return (
675
+ <div style={containerStyle}>
676
+ {filteredItems.map(item => (
677
+ <Item key={item.id} item={item} onClick={handleClick} />
678
+ ))}
679
+ </div>
680
+ );
681
+ });`;
682
+ }
683
+ // Generate image optimization config
684
+ if (issues.some(i => i.category === 'images')) {
685
+ optimizations.imageOptimizationConfig = `// Next.js image optimization config (next.config.js)
686
+ module.exports = {
687
+ images: {
688
+ domains: ['your-cdn.com'],
689
+ formats: ['image/avif', 'image/webp'],
690
+ deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840],
691
+ imageSizes: [16, 32, 48, 64, 96, 128, 256, 384],
692
+ },
693
+ };
694
+
695
+ // Optimized Image component usage:
696
+ import Image from 'next/image';
697
+
698
+ <Image
699
+ src="/hero.jpg"
700
+ alt="Hero image"
701
+ width={1200}
702
+ height={600}
703
+ priority // For LCP images
704
+ placeholder="blur"
705
+ blurDataURL="data:image/jpeg;base64,..."
706
+ />`;
707
+ }
708
+ return optimizations;
709
+ }
710
+ async function getAIPerformanceAnalysis(provider, request) {
711
+ try {
712
+ const prompt = `Analyze this ${request.framework} code for performance issues. Return JSON with additional issues not covered by static analysis:
713
+
714
+ \`\`\`
715
+ ${request.code.substring(0, 3000)}
716
+ \`\`\`
717
+
718
+ Return: { "additionalIssues": [{ "id": "AI-1", "severity": "high", "category": "render", "title": "...", "description": "...", "impact": "...", "fixedCode": "...", "estimatedImprovement": "..." }] }`;
719
+ const response = await provider.complete(prompt, { max_tokens: 1000 });
720
+ const jsonMatch = response.match(/\{[\s\S]*\}/);
721
+ if (jsonMatch) {
722
+ return JSON.parse(jsonMatch[0]);
723
+ }
724
+ }
725
+ catch (error) {
726
+ logger.debug('AI analysis failed, using heuristic results');
727
+ }
728
+ return { additionalIssues: [] };
729
+ }
730
+ export default { analyzePerformance };
731
+ //# sourceMappingURL=frontend-performance-doctor.js.map