aios-core 4.2.13 → 4.2.15
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.
- package/.aios-core/core/code-intel/helpers/dev-helper.js +206 -0
- package/.aios-core/core/registry/registry-schema.json +166 -166
- package/.aios-core/core/synapse/diagnostics/collectors/hook-collector.js +3 -3
- package/.aios-core/data/entity-registry.yaml +27 -0
- package/.aios-core/development/scripts/approval-workflow.js +642 -642
- package/.aios-core/development/scripts/backup-manager.js +606 -606
- package/.aios-core/development/scripts/branch-manager.js +389 -389
- package/.aios-core/development/scripts/code-quality-improver.js +1311 -1311
- package/.aios-core/development/scripts/commit-message-generator.js +849 -849
- package/.aios-core/development/scripts/conflict-resolver.js +674 -674
- package/.aios-core/development/scripts/dependency-analyzer.js +637 -637
- package/.aios-core/development/scripts/diff-generator.js +351 -351
- package/.aios-core/development/scripts/elicitation-engine.js +384 -384
- package/.aios-core/development/scripts/elicitation-session-manager.js +299 -299
- package/.aios-core/development/scripts/git-wrapper.js +461 -461
- package/.aios-core/development/scripts/manifest-preview.js +244 -244
- package/.aios-core/development/scripts/metrics-tracker.js +775 -775
- package/.aios-core/development/scripts/modification-validator.js +554 -554
- package/.aios-core/development/scripts/pattern-learner.js +1224 -1224
- package/.aios-core/development/scripts/performance-analyzer.js +757 -757
- package/.aios-core/development/scripts/refactoring-suggester.js +1138 -1138
- package/.aios-core/development/scripts/rollback-handler.js +530 -530
- package/.aios-core/development/scripts/security-checker.js +358 -358
- package/.aios-core/development/scripts/template-engine.js +239 -239
- package/.aios-core/development/scripts/template-validator.js +278 -278
- package/.aios-core/development/scripts/test-generator.js +843 -843
- package/.aios-core/development/scripts/transaction-manager.js +589 -589
- package/.aios-core/development/scripts/usage-tracker.js +673 -673
- package/.aios-core/development/scripts/validate-filenames.js +226 -226
- package/.aios-core/development/scripts/version-tracker.js +526 -526
- package/.aios-core/development/scripts/yaml-validator.js +396 -396
- package/.aios-core/development/tasks/build-autonomous.md +10 -4
- package/.aios-core/development/tasks/create-service.md +23 -0
- package/.aios-core/development/tasks/dev-develop-story.md +12 -6
- package/.aios-core/development/tasks/dev-suggest-refactoring.md +7 -1
- package/.aios-core/development/tasks/publish-npm.md +3 -3
- package/.aios-core/hooks/unified/README.md +1 -1
- package/.aios-core/install-manifest.yaml +65 -61
- package/.aios-core/manifests/schema/manifest-schema.json +190 -190
- package/.aios-core/product/templates/component-react-tmpl.tsx +98 -98
- package/.aios-core/product/templates/engine/schemas/adr.schema.json +102 -102
- package/.aios-core/product/templates/engine/schemas/dbdr.schema.json +205 -205
- package/.aios-core/product/templates/engine/schemas/epic.schema.json +175 -175
- package/.aios-core/product/templates/engine/schemas/pmdr.schema.json +175 -175
- package/.aios-core/product/templates/engine/schemas/prd-v2.schema.json +300 -300
- package/.aios-core/product/templates/engine/schemas/prd.schema.json +152 -152
- package/.aios-core/product/templates/engine/schemas/story.schema.json +222 -222
- package/.aios-core/product/templates/engine/schemas/task.schema.json +154 -154
- package/.aios-core/product/templates/eslintrc-security.json +32 -32
- package/.aios-core/product/templates/github-actions-cd.yml +212 -212
- package/.aios-core/product/templates/github-actions-ci.yml +172 -172
- package/.aios-core/product/templates/shock-report-tmpl.html +502 -502
- package/.aios-core/product/templates/token-exports-css-tmpl.css +240 -240
- package/.aios-core/quality/schemas/quality-metrics.schema.json +233 -233
- package/.aios-core/scripts/migrate-framework-docs.sh +300 -300
- package/README.en.md +747 -0
- package/README.md +4 -2
- package/bin/aios.js +7 -4
- package/package.json +1 -1
- package/packages/aios-pro-cli/src/recover.js +1 -1
- package/packages/installer/src/wizard/ide-config-generator.js +6 -6
- package/packages/installer/src/wizard/pro-setup.js +3 -3
- package/pro/license/degradation.js +220 -220
- package/pro/license/errors.js +450 -450
- package/pro/license/feature-gate.js +354 -354
- package/pro/license/index.js +181 -181
- package/pro/license/license-cache.js +523 -523
- package/pro/license/license-crypto.js +303 -303
- package/scripts/package-synapse.js +5 -5
- package/scripts/validate-package-completeness.js +3 -3
- package/.aios-core/.session/current-session.json +0 -14
- package/.aios-core/data/registry-update-log.jsonl +0 -191
- package/.aios-core/docs/SHARD-TRANSLATION-GUIDE.md +0 -335
- package/.aios-core/docs/component-creation-guide.md +0 -458
- package/.aios-core/docs/session-update-pattern.md +0 -307
- package/.aios-core/docs/standards/AIOS-FRAMEWORK-MASTER.md +0 -1963
- package/.aios-core/docs/standards/AIOS-LIVRO-DE-OURO-V2.1-SUMMARY.md +0 -1190
- package/.aios-core/docs/standards/AIOS-LIVRO-DE-OURO-V2.1.md +0 -439
- package/.aios-core/docs/standards/AIOS-LIVRO-DE-OURO.md +0 -5398
- package/.aios-core/docs/standards/V3-ARCHITECTURAL-DECISIONS.md +0 -523
- package/.aios-core/docs/template-syntax.md +0 -267
- package/.aios-core/docs/troubleshooting-guide.md +0 -625
- package/.aios-core/infrastructure/tests/utilities-audit-results.json +0 -501
- package/.aios-core/manifests/agents.csv +0 -29
- package/.aios-core/manifests/tasks.csv +0 -198
- package/.aios-core/manifests/workers.csv +0 -204
- package/.claude/rules/agent-authority.md +0 -105
- package/.claude/rules/coderabbit-integration.md +0 -93
- package/.claude/rules/ids-principles.md +0 -112
- package/.claude/rules/story-lifecycle.md +0 -139
- package/.claude/rules/workflow-execution.md +0 -150
- package/scripts/glue/README.md +0 -355
- package/scripts/glue/compose-agent-prompt.cjs +0 -362
- /package/.claude/hooks/{precompact-session-digest.js → precompact-session-digest.cjs} +0 -0
- /package/.claude/hooks/{synapse-engine.js → synapse-engine.cjs} +0 -0
|
@@ -1,758 +1,758 @@
|
|
|
1
|
-
const fs = require('fs').promises;
|
|
2
|
-
const path = require('path');
|
|
3
|
-
const chalk = require('chalk');
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Performance bottleneck analyzer for AIOS-FULLSTACK framework
|
|
7
|
-
* Identifies performance issues and optimization opportunities
|
|
8
|
-
*/
|
|
9
|
-
class PerformanceAnalyzer {
|
|
10
|
-
constructor(options = {}) {
|
|
11
|
-
this.rootPath = options.rootPath || process.cwd();
|
|
12
|
-
this.thresholds = {
|
|
13
|
-
fileSize: 50 * 1024, // 50KB
|
|
14
|
-
functionLength: 50, // lines
|
|
15
|
-
cyclomaticComplexity: 10,
|
|
16
|
-
nestingDepth: 5,
|
|
17
|
-
importCount: 20,
|
|
18
|
-
unusedCode: 0.1 // 10% unused code ratio
|
|
19
|
-
};
|
|
20
|
-
this.performancePatterns = {
|
|
21
|
-
// Sync operations that should be async
|
|
22
|
-
syncOperations: [
|
|
23
|
-
/fs\.readFileSync/g,
|
|
24
|
-
/fs\.writeFileSync/g,
|
|
25
|
-
/fs\.existsSync/g,
|
|
26
|
-
/JSON\.parse\(\s*fs\.readFileSync/g
|
|
27
|
-
],
|
|
28
|
-
// Memory-intensive patterns
|
|
29
|
-
memoryIssues: [
|
|
30
|
-
/\.map\(\s*.*\.map\(/g, // Nested maps
|
|
31
|
-
/new Array\(\d{4,}\)/g, // Large arrays
|
|
32
|
-
/JSON\.stringify.*JSON\.parse/g, // Unnecessary serialization
|
|
33
|
-
/require\(\s*['"`][^'"`]+\.json['"`]\s*\)/g // Large JSON imports
|
|
34
|
-
],
|
|
35
|
-
// Performance anti-patterns
|
|
36
|
-
antiPatterns: [
|
|
37
|
-
/console\.log/g, // Console logs in production
|
|
38
|
-
/debugger/g, // Debugger statements
|
|
39
|
-
/setTimeout\(\s*.*,\s*0\s*\)/g, // Unnecessary setTimeout
|
|
40
|
-
/setInterval\(\s*.*,\s*[0-9]{1,2}\s*\)/g // High-frequency intervals
|
|
41
|
-
],
|
|
42
|
-
// Inefficient loops
|
|
43
|
-
inefficientLoops: [
|
|
44
|
-
/for\s*\(\s*.*\.length\s*;/g, // Length calculated in loop condition
|
|
45
|
-
/while\s*\(\s*true\s*\)/g, // Infinite loops
|
|
46
|
-
/forEach.*forEach/g // Nested forEach
|
|
47
|
-
]
|
|
48
|
-
};
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Analyze performance across all components
|
|
53
|
-
*/
|
|
54
|
-
async analyzePerformance(components) {
|
|
55
|
-
const analysis = {
|
|
56
|
-
timestamp: new Date().toISOString(),
|
|
57
|
-
overall_score: 0,
|
|
58
|
-
bottlenecks: [],
|
|
59
|
-
optimization_opportunities: [],
|
|
60
|
-
memory_issues: [],
|
|
61
|
-
async_opportunities: [],
|
|
62
|
-
code_quality_issues: [],
|
|
63
|
-
file_size_issues: [],
|
|
64
|
-
complexity_issues: [],
|
|
65
|
-
recommendations: [],
|
|
66
|
-
metrics: {
|
|
67
|
-
total_files_analyzed: 0,
|
|
68
|
-
total_issues_found: 0,
|
|
69
|
-
critical_issues: 0,
|
|
70
|
-
high_priority_issues: 0,
|
|
71
|
-
medium_priority_issues: 0,
|
|
72
|
-
low_priority_issues: 0
|
|
73
|
-
}
|
|
74
|
-
};
|
|
75
|
-
|
|
76
|
-
try {
|
|
77
|
-
console.log(chalk.blue('🔍 Analyzing performance bottlenecks...'));
|
|
78
|
-
|
|
79
|
-
// Analyze each component
|
|
80
|
-
for (const component of components) {
|
|
81
|
-
const componentAnalysis = await this.analyzeComponentPerformance(component);
|
|
82
|
-
|
|
83
|
-
// Aggregate results
|
|
84
|
-
analysis.bottlenecks.push(...componentAnalysis.bottlenecks);
|
|
85
|
-
analysis.optimization_opportunities.push(...componentAnalysis.optimizations);
|
|
86
|
-
analysis.memory_issues.push(...componentAnalysis.memory_issues);
|
|
87
|
-
analysis.async_opportunities.push(...componentAnalysis.async_opportunities);
|
|
88
|
-
analysis.code_quality_issues.push(...componentAnalysis.quality_issues);
|
|
89
|
-
analysis.file_size_issues.push(...componentAnalysis.file_size_issues);
|
|
90
|
-
analysis.complexity_issues.push(...componentAnalysis.complexity_issues);
|
|
91
|
-
|
|
92
|
-
analysis.metrics.total_files_analyzed++;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
// Calculate overall metrics
|
|
96
|
-
analysis.overall_score = this.calculateOverallScore(analysis);
|
|
97
|
-
analysis.metrics.total_issues_found = this.countTotalIssues(analysis);
|
|
98
|
-
analysis.metrics = this.calculateIssuePriorities(analysis);
|
|
99
|
-
analysis.recommendations = this.generatePerformanceRecommendations(analysis);
|
|
100
|
-
|
|
101
|
-
console.log(chalk.green(`✅ Performance analysis completed`));
|
|
102
|
-
console.log(chalk.gray(` Files analyzed: ${analysis.metrics.total_files_analyzed}`));
|
|
103
|
-
console.log(chalk.gray(` Issues found: ${analysis.metrics.total_issues_found}`));
|
|
104
|
-
console.log(chalk.gray(` Overall score: ${analysis.overall_score}/10`));
|
|
105
|
-
|
|
106
|
-
return analysis;
|
|
107
|
-
|
|
108
|
-
} catch (error) {
|
|
109
|
-
console.error(chalk.red(`Performance analysis failed: ${error.message}`));
|
|
110
|
-
throw error;
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* Analyze performance for a single component
|
|
116
|
-
*/
|
|
117
|
-
async analyzeComponentPerformance(component) {
|
|
118
|
-
const analysis = {
|
|
119
|
-
component_id: component.id,
|
|
120
|
-
component_type: component.type,
|
|
121
|
-
bottlenecks: [],
|
|
122
|
-
optimizations: [],
|
|
123
|
-
memory_issues: [],
|
|
124
|
-
async_opportunities: [],
|
|
125
|
-
quality_issues: [],
|
|
126
|
-
file_size_issues: [],
|
|
127
|
-
complexity_issues: []
|
|
128
|
-
};
|
|
129
|
-
|
|
130
|
-
try {
|
|
131
|
-
// Only analyze JavaScript files and executable components
|
|
132
|
-
if (!this.shouldAnalyzeComponent(component)) {
|
|
133
|
-
return analysis;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
const filePath = path.join(this.rootPath, component.file_path);
|
|
137
|
-
const content = await fs.readFile(filePath, 'utf-8');
|
|
138
|
-
const stats = await fs.stat(filePath);
|
|
139
|
-
|
|
140
|
-
// File size analysis
|
|
141
|
-
if (stats.size > this.thresholds.fileSize) {
|
|
142
|
-
analysis.file_size_issues.push({
|
|
143
|
-
component: component.id,
|
|
144
|
-
issue: `Large file size: ${this.formatBytes(stats.size)}`,
|
|
145
|
-
severity: stats.size > this.thresholds.fileSize * 2 ? 'high' : 'medium',
|
|
146
|
-
recommendation: 'Consider breaking down into smaller modules',
|
|
147
|
-
impact: 'high',
|
|
148
|
-
effort: 'medium'
|
|
149
|
-
});
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
// Synchronous operations analysis
|
|
153
|
-
const syncIssues = this.findSyncOperations(content, component);
|
|
154
|
-
analysis.async_opportunities.push(...syncIssues);
|
|
155
|
-
|
|
156
|
-
// Memory usage analysis
|
|
157
|
-
const memoryIssues = this.findMemoryIssues(content, component);
|
|
158
|
-
analysis.memory_issues.push(...memoryIssues);
|
|
159
|
-
|
|
160
|
-
// Performance anti-patterns
|
|
161
|
-
const antiPatterns = this.findAntiPatterns(content, component);
|
|
162
|
-
analysis.quality_issues.push(...antiPatterns);
|
|
163
|
-
|
|
164
|
-
// Loop efficiency analysis
|
|
165
|
-
const loopIssues = this.findLoopIssues(content, component);
|
|
166
|
-
analysis.bottlenecks.push(...loopIssues);
|
|
167
|
-
|
|
168
|
-
// Function complexity analysis
|
|
169
|
-
const complexityIssues = this.analyzeComplexity(content, component);
|
|
170
|
-
analysis.complexity_issues.push(...complexityIssues);
|
|
171
|
-
|
|
172
|
-
// Import/require analysis
|
|
173
|
-
const importIssues = this.analyzeImports(content, component);
|
|
174
|
-
analysis.optimizations.push(...importIssues);
|
|
175
|
-
|
|
176
|
-
// Dead code analysis
|
|
177
|
-
const deadCodeIssues = await this.findDeadCode(content, component);
|
|
178
|
-
analysis.optimizations.push(...deadCodeIssues);
|
|
179
|
-
|
|
180
|
-
} catch (error) {
|
|
181
|
-
console.warn(chalk.yellow(`Failed to analyze performance for ${component.id}: ${error.message}`));
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
return analysis;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
/**
|
|
188
|
-
* Find synchronous operations that should be async
|
|
189
|
-
*/
|
|
190
|
-
findSyncOperations(content, component) {
|
|
191
|
-
const issues = [];
|
|
192
|
-
const lines = content.split('\n');
|
|
193
|
-
|
|
194
|
-
this.performancePatterns.syncOperations.forEach(pattern => {
|
|
195
|
-
lines.forEach((line, index) => {
|
|
196
|
-
const matches = line.match(pattern);
|
|
197
|
-
if (matches) {
|
|
198
|
-
matches.forEach(match => {
|
|
199
|
-
issues.push({
|
|
200
|
-
component: component.id,
|
|
201
|
-
issue: `Synchronous operation: ${match}`,
|
|
202
|
-
line: index + 1,
|
|
203
|
-
context: line.trim(),
|
|
204
|
-
severity: 'high',
|
|
205
|
-
recommendation: 'Convert to async equivalent',
|
|
206
|
-
impact: 'high',
|
|
207
|
-
effort: 'low'
|
|
208
|
-
});
|
|
209
|
-
});
|
|
210
|
-
}
|
|
211
|
-
});
|
|
212
|
-
});
|
|
213
|
-
|
|
214
|
-
return issues;
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
/**
|
|
218
|
-
* Find memory-intensive patterns
|
|
219
|
-
*/
|
|
220
|
-
findMemoryIssues(content, component) {
|
|
221
|
-
const issues = [];
|
|
222
|
-
const lines = content.split('\n');
|
|
223
|
-
|
|
224
|
-
this.performancePatterns.memoryIssues.forEach(pattern => {
|
|
225
|
-
lines.forEach((line, index) => {
|
|
226
|
-
const matches = line.match(pattern);
|
|
227
|
-
if (matches) {
|
|
228
|
-
matches.forEach(match => {
|
|
229
|
-
let severity = 'medium';
|
|
230
|
-
let recommendation = 'Optimize memory usage';
|
|
231
|
-
|
|
232
|
-
if (match.includes('new Array')) {
|
|
233
|
-
severity = 'high';
|
|
234
|
-
recommendation = 'Use array literals or streaming for large datasets';
|
|
235
|
-
} else if (match.includes('JSON.stringify.*JSON.parse')) {
|
|
236
|
-
recommendation = 'Use direct object assignment or deep clone utilities';
|
|
237
|
-
} else if (match.includes('.map(.*map(')) {
|
|
238
|
-
recommendation = 'Combine operations or use more efficient iteration';
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
issues.push({
|
|
242
|
-
component: component.id,
|
|
243
|
-
issue: `Memory-intensive pattern: ${match}`,
|
|
244
|
-
line: index + 1,
|
|
245
|
-
context: line.trim(),
|
|
246
|
-
severity,
|
|
247
|
-
recommendation,
|
|
248
|
-
impact: 'medium',
|
|
249
|
-
effort: 'medium'
|
|
250
|
-
});
|
|
251
|
-
});
|
|
252
|
-
}
|
|
253
|
-
});
|
|
254
|
-
});
|
|
255
|
-
|
|
256
|
-
return issues;
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
/**
|
|
260
|
-
* Find performance anti-patterns
|
|
261
|
-
*/
|
|
262
|
-
findAntiPatterns(content, component) {
|
|
263
|
-
const issues = [];
|
|
264
|
-
const lines = content.split('\n');
|
|
265
|
-
|
|
266
|
-
this.performancePatterns.antiPatterns.forEach(pattern => {
|
|
267
|
-
lines.forEach((line, index) => {
|
|
268
|
-
const matches = line.match(pattern);
|
|
269
|
-
if (matches) {
|
|
270
|
-
matches.forEach(match => {
|
|
271
|
-
let severity = 'low';
|
|
272
|
-
let recommendation = 'Remove or optimize';
|
|
273
|
-
|
|
274
|
-
if (match.includes('console.log')) {
|
|
275
|
-
recommendation = 'Remove console.log statements from production code';
|
|
276
|
-
} else if (match.includes('debugger')) {
|
|
277
|
-
severity = 'medium';
|
|
278
|
-
recommendation = 'Remove debugger statements';
|
|
279
|
-
} else if (match.includes('setTimeout')) {
|
|
280
|
-
recommendation = 'Use proper async/await or promises instead';
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
issues.push({
|
|
284
|
-
component: component.id,
|
|
285
|
-
issue: `Performance anti-pattern: ${match}`,
|
|
286
|
-
line: index + 1,
|
|
287
|
-
context: line.trim(),
|
|
288
|
-
severity,
|
|
289
|
-
recommendation,
|
|
290
|
-
impact: 'low',
|
|
291
|
-
effort: 'low'
|
|
292
|
-
});
|
|
293
|
-
});
|
|
294
|
-
}
|
|
295
|
-
});
|
|
296
|
-
});
|
|
297
|
-
|
|
298
|
-
return issues;
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
/**
|
|
302
|
-
* Find inefficient loop patterns
|
|
303
|
-
*/
|
|
304
|
-
findLoopIssues(content, component) {
|
|
305
|
-
const issues = [];
|
|
306
|
-
const lines = content.split('\n');
|
|
307
|
-
|
|
308
|
-
this.performancePatterns.inefficientLoops.forEach(pattern => {
|
|
309
|
-
lines.forEach((line, index) => {
|
|
310
|
-
const matches = line.match(pattern);
|
|
311
|
-
if (matches) {
|
|
312
|
-
matches.forEach(match => {
|
|
313
|
-
let severity = 'medium';
|
|
314
|
-
let recommendation = 'Optimize loop structure';
|
|
315
|
-
|
|
316
|
-
if (match.includes('.length')) {
|
|
317
|
-
recommendation = 'Cache array length in variable before loop';
|
|
318
|
-
} else if (match.includes('while(true)')) {
|
|
319
|
-
severity = 'high';
|
|
320
|
-
recommendation = 'Add proper exit condition to prevent infinite loops';
|
|
321
|
-
} else if (match.includes('forEach.*forEach')) {
|
|
322
|
-
recommendation = 'Consider using nested for loops or array methods like flatMap';
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
issues.push({
|
|
326
|
-
component: component.id,
|
|
327
|
-
issue: `Inefficient loop: ${match}`,
|
|
328
|
-
line: index + 1,
|
|
329
|
-
context: line.trim(),
|
|
330
|
-
severity,
|
|
331
|
-
recommendation,
|
|
332
|
-
impact: 'medium',
|
|
333
|
-
effort: 'low'
|
|
334
|
-
});
|
|
335
|
-
});
|
|
336
|
-
}
|
|
337
|
-
});
|
|
338
|
-
});
|
|
339
|
-
|
|
340
|
-
return issues;
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
/**
|
|
344
|
-
* Analyze function complexity
|
|
345
|
-
*/
|
|
346
|
-
analyzeComplexity(content, component) {
|
|
347
|
-
const issues = [];
|
|
348
|
-
const functions = this.extractFunctions(content);
|
|
349
|
-
|
|
350
|
-
functions.forEach(func => {
|
|
351
|
-
// Check function length
|
|
352
|
-
if (func.lines > this.thresholds.functionLength) {
|
|
353
|
-
issues.push({
|
|
354
|
-
component: component.id,
|
|
355
|
-
issue: `Long function: ${func.name} (${func.lines} lines)`,
|
|
356
|
-
line: func.startLine,
|
|
357
|
-
severity: func.lines > this.thresholds.functionLength * 2 ? 'high' : 'medium',
|
|
358
|
-
recommendation: 'Break down into smaller functions',
|
|
359
|
-
impact: 'medium',
|
|
360
|
-
effort: 'medium'
|
|
361
|
-
});
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
// Check cyclomatic complexity
|
|
365
|
-
if (func.complexity > this.thresholds.cyclomaticComplexity) {
|
|
366
|
-
issues.push({
|
|
367
|
-
component: component.id,
|
|
368
|
-
issue: `High complexity: ${func.name} (complexity: ${func.complexity})`,
|
|
369
|
-
line: func.startLine,
|
|
370
|
-
severity: func.complexity > this.thresholds.cyclomaticComplexity * 1.5 ? 'high' : 'medium',
|
|
371
|
-
recommendation: 'Reduce conditional complexity',
|
|
372
|
-
impact: 'high',
|
|
373
|
-
effort: 'high'
|
|
374
|
-
});
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
// Check nesting depth
|
|
378
|
-
if (func.nestingDepth > this.thresholds.nestingDepth) {
|
|
379
|
-
issues.push({
|
|
380
|
-
component: component.id,
|
|
381
|
-
issue: `Deep nesting: ${func.name} (depth: ${func.nestingDepth})`,
|
|
382
|
-
line: func.startLine,
|
|
383
|
-
severity: 'medium',
|
|
384
|
-
recommendation: 'Reduce nesting depth using early returns or guard clauses',
|
|
385
|
-
impact: 'medium',
|
|
386
|
-
effort: 'medium'
|
|
387
|
-
});
|
|
388
|
-
}
|
|
389
|
-
});
|
|
390
|
-
|
|
391
|
-
return issues;
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
/**
|
|
395
|
-
* Analyze imports and requires
|
|
396
|
-
*/
|
|
397
|
-
analyzeImports(content, component) {
|
|
398
|
-
const issues = [];
|
|
399
|
-
const imports = this.extractImports(content);
|
|
400
|
-
|
|
401
|
-
if (imports.length > this.thresholds.importCount) {
|
|
402
|
-
issues.push({
|
|
403
|
-
component: component.id,
|
|
404
|
-
issue: `Too many imports: ${imports.length}`,
|
|
405
|
-
severity: 'medium',
|
|
406
|
-
recommendation: 'Consider breaking down module or using dynamic imports',
|
|
407
|
-
impact: 'low',
|
|
408
|
-
effort: 'medium'
|
|
409
|
-
});
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
// Find unused imports (basic heuristic)
|
|
413
|
-
const unusedImports = imports.filter(imp => {
|
|
414
|
-
const usage = content.split(imp.name).length - 1;
|
|
415
|
-
return usage <= 1; // Only appears in import statement
|
|
416
|
-
});
|
|
417
|
-
|
|
418
|
-
if (unusedImports.length > 0) {
|
|
419
|
-
issues.push({
|
|
420
|
-
component: component.id,
|
|
421
|
-
issue: `Unused imports: ${unusedImports.map(i => i.name).join(', ')}`,
|
|
422
|
-
severity: 'low',
|
|
423
|
-
recommendation: 'Remove unused imports',
|
|
424
|
-
impact: 'low',
|
|
425
|
-
effort: 'low'
|
|
426
|
-
});
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
return issues;
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
/**
|
|
433
|
-
* Find dead code (unused functions, variables)
|
|
434
|
-
*/
|
|
435
|
-
async findDeadCode(content, component) {
|
|
436
|
-
const issues = [];
|
|
437
|
-
|
|
438
|
-
// This is a simplified implementation
|
|
439
|
-
// A full implementation would analyze the entire codebase for usage
|
|
440
|
-
const functions = this.extractFunctions(content);
|
|
441
|
-
const exports = this.extractExports(content);
|
|
442
|
-
|
|
443
|
-
// Check for functions that are defined but never called within the file
|
|
444
|
-
functions.forEach(func => {
|
|
445
|
-
if (!func.name.startsWith('_') && !exports.includes(func.name)) {
|
|
446
|
-
const usage = content.split(func.name).length - 1;
|
|
447
|
-
if (usage <= 1) { // Only appears in definition
|
|
448
|
-
issues.push({
|
|
449
|
-
component: component.id,
|
|
450
|
-
issue: `Potentially unused function: ${func.name}`,
|
|
451
|
-
line: func.startLine,
|
|
452
|
-
severity: 'low',
|
|
453
|
-
recommendation: 'Remove if truly unused or export if needed elsewhere',
|
|
454
|
-
impact: 'low',
|
|
455
|
-
effort: 'low'
|
|
456
|
-
});
|
|
457
|
-
}
|
|
458
|
-
}
|
|
459
|
-
});
|
|
460
|
-
|
|
461
|
-
return issues;
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
/**
|
|
465
|
-
* Extract function information from code
|
|
466
|
-
*/
|
|
467
|
-
extractFunctions(content) {
|
|
468
|
-
const functions = [];
|
|
469
|
-
const lines = content.split('\n');
|
|
470
|
-
const functionRegex = /(?:function\s+(\w+)|(\w+)\s*[:=]\s*(?:async\s+)?function|(?:async\s+)?(\w+)\s*\([^)]*\)\s*(?:=>|{))/g;
|
|
471
|
-
|
|
472
|
-
lines.forEach((line, index) => {
|
|
473
|
-
const matches = [...line.matchAll(functionRegex)];
|
|
474
|
-
matches.forEach(match => {
|
|
475
|
-
const name = match[1] || match[2] || match[3] || 'anonymous';
|
|
476
|
-
|
|
477
|
-
// Calculate function metrics (simplified)
|
|
478
|
-
const funcInfo = {
|
|
479
|
-
name,
|
|
480
|
-
startLine: index + 1,
|
|
481
|
-
lines: this.estimateFunctionLength(lines, index),
|
|
482
|
-
complexity: this.estimateComplexity(lines, index),
|
|
483
|
-
nestingDepth: this.estimateNestingDepth(lines, index)
|
|
484
|
-
};
|
|
485
|
-
|
|
486
|
-
functions.push(funcInfo);
|
|
487
|
-
});
|
|
488
|
-
});
|
|
489
|
-
|
|
490
|
-
return functions;
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
/**
|
|
494
|
-
* Extract import information
|
|
495
|
-
*/
|
|
496
|
-
extractImports(content) {
|
|
497
|
-
const imports = [];
|
|
498
|
-
const importRegex = /(?:import\s+(?:(\w+)|\{([^}]+)\}|.*)\s+from\s+['"`]([^'"`]+)['"`]|const\s+(?:(\w+)|\{([^}]+)\})\s*=\s*require\(['"`]([^'"`]+)['"`]\))/g;
|
|
499
|
-
|
|
500
|
-
const matches = [...content.matchAll(importRegex)];
|
|
501
|
-
matches.forEach(match => {
|
|
502
|
-
const defaultImport = match[1] || match[4];
|
|
503
|
-
const namedImports = match[2] || match[5];
|
|
504
|
-
const source = match[3] || match[6];
|
|
505
|
-
|
|
506
|
-
if (defaultImport) {
|
|
507
|
-
imports.push({ name: defaultImport, source, type: 'default' });
|
|
508
|
-
}
|
|
509
|
-
|
|
510
|
-
if (namedImports) {
|
|
511
|
-
namedImports.split(',').forEach(name => {
|
|
512
|
-
imports.push({ name: name.trim(), source, type: 'named' });
|
|
513
|
-
});
|
|
514
|
-
}
|
|
515
|
-
});
|
|
516
|
-
|
|
517
|
-
return imports;
|
|
518
|
-
}
|
|
519
|
-
|
|
520
|
-
/**
|
|
521
|
-
* Extract export information
|
|
522
|
-
*/
|
|
523
|
-
extractExports(content) {
|
|
524
|
-
const exports = [];
|
|
525
|
-
const exportRegex = /(?:export\s+(?:default\s+)?(?:function\s+(\w+)|class\s+(\w+)|(?:const|let|var)\s+(\w+))|module\.exports\s*=\s*(\w+))/g;
|
|
526
|
-
|
|
527
|
-
const matches = [...content.matchAll(exportRegex)];
|
|
528
|
-
matches.forEach(match => {
|
|
529
|
-
const name = match[1] || match[2] || match[3] || match[4];
|
|
530
|
-
if (name) exports.push(name);
|
|
531
|
-
});
|
|
532
|
-
|
|
533
|
-
return exports;
|
|
534
|
-
}
|
|
535
|
-
|
|
536
|
-
/**
|
|
537
|
-
* Estimate function length (simplified)
|
|
538
|
-
*/
|
|
539
|
-
estimateFunctionLength(lines, startIndex) {
|
|
540
|
-
let braceCount = 0;
|
|
541
|
-
let lineCount = 0;
|
|
542
|
-
let inFunction = false;
|
|
543
|
-
|
|
544
|
-
for (let i = startIndex; i < lines.length; i++) {
|
|
545
|
-
const line = lines[i];
|
|
546
|
-
lineCount++;
|
|
547
|
-
|
|
548
|
-
// Count braces to find function end
|
|
549
|
-
for (const char of line) {
|
|
550
|
-
if (char === '{') {
|
|
551
|
-
braceCount++;
|
|
552
|
-
inFunction = true;
|
|
553
|
-
} else if (char === '}') {
|
|
554
|
-
braceCount--;
|
|
555
|
-
if (inFunction && braceCount === 0) {
|
|
556
|
-
return lineCount;
|
|
557
|
-
}
|
|
558
|
-
}
|
|
559
|
-
}
|
|
560
|
-
|
|
561
|
-
// Safety limit
|
|
562
|
-
if (lineCount > 200) break;
|
|
563
|
-
}
|
|
564
|
-
|
|
565
|
-
return lineCount;
|
|
566
|
-
}
|
|
567
|
-
|
|
568
|
-
/**
|
|
569
|
-
* Estimate cyclomatic complexity (simplified)
|
|
570
|
-
*/
|
|
571
|
-
estimateComplexity(lines, startIndex) {
|
|
572
|
-
let complexity = 1; // Base complexity
|
|
573
|
-
const complexityKeywords = ['if', 'else', 'while', 'for', 'switch', 'case', 'catch', '&&', '||', '?'];
|
|
574
|
-
|
|
575
|
-
const functionLines = lines.slice(startIndex, startIndex + this.estimateFunctionLength(lines, startIndex));
|
|
576
|
-
|
|
577
|
-
functionLines.forEach(line => {
|
|
578
|
-
complexityKeywords.forEach(keyword => {
|
|
579
|
-
const matches = line.match(new RegExp(`\\b${keyword}\\b`, 'g'));
|
|
580
|
-
if (matches) {
|
|
581
|
-
complexity += matches.length;
|
|
582
|
-
}
|
|
583
|
-
});
|
|
584
|
-
});
|
|
585
|
-
|
|
586
|
-
return complexity;
|
|
587
|
-
}
|
|
588
|
-
|
|
589
|
-
/**
|
|
590
|
-
* Estimate nesting depth (simplified)
|
|
591
|
-
*/
|
|
592
|
-
estimateNestingDepth(lines, startIndex) {
|
|
593
|
-
let maxDepth = 0;
|
|
594
|
-
let currentDepth = 0;
|
|
595
|
-
|
|
596
|
-
const functionLines = lines.slice(startIndex, startIndex + this.estimateFunctionLength(lines, startIndex));
|
|
597
|
-
|
|
598
|
-
functionLines.forEach(line => {
|
|
599
|
-
for (const char of line) {
|
|
600
|
-
if (char === '{') {
|
|
601
|
-
currentDepth++;
|
|
602
|
-
maxDepth = Math.max(maxDepth, currentDepth);
|
|
603
|
-
} else if (char === '}') {
|
|
604
|
-
currentDepth--;
|
|
605
|
-
}
|
|
606
|
-
}
|
|
607
|
-
});
|
|
608
|
-
|
|
609
|
-
return maxDepth;
|
|
610
|
-
}
|
|
611
|
-
|
|
612
|
-
/**
|
|
613
|
-
* Calculate overall performance score
|
|
614
|
-
*/
|
|
615
|
-
calculateOverallScore(analysis) {
|
|
616
|
-
let score = 10;
|
|
617
|
-
const penalties = {
|
|
618
|
-
critical: 2,
|
|
619
|
-
high: 1,
|
|
620
|
-
medium: 0.5,
|
|
621
|
-
low: 0.1
|
|
622
|
-
};
|
|
623
|
-
|
|
624
|
-
// Apply penalties based on issue severity
|
|
625
|
-
[...analysis.bottlenecks, ...analysis.memory_issues, ...analysis.async_opportunities,
|
|
626
|
-
...analysis.code_quality_issues, ...analysis.file_size_issues, ...analysis.complexity_issues]
|
|
627
|
-
.forEach(issue => {
|
|
628
|
-
score -= penalties[issue.severity] || 0.1;
|
|
629
|
-
});
|
|
630
|
-
|
|
631
|
-
return Math.max(0, Math.round(score * 10) / 10);
|
|
632
|
-
}
|
|
633
|
-
|
|
634
|
-
/**
|
|
635
|
-
* Count total issues
|
|
636
|
-
*/
|
|
637
|
-
countTotalIssues(analysis) {
|
|
638
|
-
return analysis.bottlenecks.length +
|
|
639
|
-
analysis.memory_issues.length +
|
|
640
|
-
analysis.async_opportunities.length +
|
|
641
|
-
analysis.code_quality_issues.length +
|
|
642
|
-
analysis.file_size_issues.length +
|
|
643
|
-
analysis.complexity_issues.length;
|
|
644
|
-
}
|
|
645
|
-
|
|
646
|
-
/**
|
|
647
|
-
* Calculate issue priorities
|
|
648
|
-
*/
|
|
649
|
-
calculateIssuePriorities(analysis) {
|
|
650
|
-
const metrics = analysis.metrics;
|
|
651
|
-
const allIssues = [
|
|
652
|
-
...analysis.bottlenecks,
|
|
653
|
-
...analysis.memory_issues,
|
|
654
|
-
...analysis.async_opportunities,
|
|
655
|
-
...analysis.code_quality_issues,
|
|
656
|
-
...analysis.file_size_issues,
|
|
657
|
-
...analysis.complexity_issues
|
|
658
|
-
];
|
|
659
|
-
|
|
660
|
-
metrics.critical_issues = allIssues.filter(i => i.severity === 'critical').length;
|
|
661
|
-
metrics.high_priority_issues = allIssues.filter(i => i.severity === 'high').length;
|
|
662
|
-
metrics.medium_priority_issues = allIssues.filter(i => i.severity === 'medium').length;
|
|
663
|
-
metrics.low_priority_issues = allIssues.filter(i => i.severity === 'low').length;
|
|
664
|
-
|
|
665
|
-
return metrics;
|
|
666
|
-
}
|
|
667
|
-
|
|
668
|
-
/**
|
|
669
|
-
* Generate performance recommendations
|
|
670
|
-
*/
|
|
671
|
-
generatePerformanceRecommendations(analysis) {
|
|
672
|
-
const recommendations = [];
|
|
673
|
-
|
|
674
|
-
// Critical issues
|
|
675
|
-
if (analysis.metrics.critical_issues > 0) {
|
|
676
|
-
recommendations.push({
|
|
677
|
-
priority: 'critical',
|
|
678
|
-
category: 'immediate_action',
|
|
679
|
-
message: `${analysis.metrics.critical_issues} critical performance issues require immediate attention`,
|
|
680
|
-
action: 'Fix critical bottlenecks before deployment'
|
|
681
|
-
});
|
|
682
|
-
}
|
|
683
|
-
|
|
684
|
-
// High severity issues
|
|
685
|
-
if (analysis.metrics.high_priority_issues > 5) {
|
|
686
|
-
recommendations.push({
|
|
687
|
-
priority: 'high',
|
|
688
|
-
category: 'performance',
|
|
689
|
-
message: `${analysis.metrics.high_priority_issues} high-priority performance issues detected`,
|
|
690
|
-
action: 'Address synchronous operations and complexity issues'
|
|
691
|
-
});
|
|
692
|
-
}
|
|
693
|
-
|
|
694
|
-
// Memory issues
|
|
695
|
-
if (analysis.memory_issues.length > 0) {
|
|
696
|
-
recommendations.push({
|
|
697
|
-
priority: 'medium',
|
|
698
|
-
category: 'memory',
|
|
699
|
-
message: `${analysis.memory_issues.length} memory optimization opportunities found`,
|
|
700
|
-
action: 'Implement memory-efficient patterns and data structures'
|
|
701
|
-
});
|
|
702
|
-
}
|
|
703
|
-
|
|
704
|
-
// File size issues
|
|
705
|
-
if (analysis.file_size_issues.length > 0) {
|
|
706
|
-
recommendations.push({
|
|
707
|
-
priority: 'medium',
|
|
708
|
-
category: 'modularity',
|
|
709
|
-
message: `${analysis.file_size_issues.length} files are too large`,
|
|
710
|
-
action: 'Break down large files into smaller, focused modules'
|
|
711
|
-
});
|
|
712
|
-
}
|
|
713
|
-
|
|
714
|
-
// Overall score
|
|
715
|
-
if (analysis.overall_score < 7) {
|
|
716
|
-
recommendations.push({
|
|
717
|
-
priority: 'high',
|
|
718
|
-
category: 'general',
|
|
719
|
-
message: `Overall performance score is ${analysis.overall_score}/10`,
|
|
720
|
-
action: 'Comprehensive performance review and optimization needed'
|
|
721
|
-
});
|
|
722
|
-
}
|
|
723
|
-
|
|
724
|
-
return recommendations;
|
|
725
|
-
}
|
|
726
|
-
|
|
727
|
-
/**
|
|
728
|
-
* Check if component should be analyzed
|
|
729
|
-
*/
|
|
730
|
-
shouldAnalyzeComponent(component) {
|
|
731
|
-
const analyzableTypes = ['utility', 'task'];
|
|
732
|
-
const analyzableExtensions = ['.js', '.mjs', '.ts'];
|
|
733
|
-
|
|
734
|
-
if (!analyzableTypes.includes(component.type)) {
|
|
735
|
-
return false;
|
|
736
|
-
}
|
|
737
|
-
|
|
738
|
-
if (component.file_path) {
|
|
739
|
-
const ext = path.extname(component.file_path).toLowerCase();
|
|
740
|
-
return analyzableExtensions.includes(ext);
|
|
741
|
-
}
|
|
742
|
-
|
|
743
|
-
return false;
|
|
744
|
-
}
|
|
745
|
-
|
|
746
|
-
/**
|
|
747
|
-
* Format bytes for display
|
|
748
|
-
*/
|
|
749
|
-
formatBytes(bytes) {
|
|
750
|
-
if (bytes === 0) return '0 B';
|
|
751
|
-
const k = 1024;
|
|
752
|
-
const sizes = ['B', 'KB', 'MB', 'GB'];
|
|
753
|
-
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
754
|
-
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
|
|
755
|
-
}
|
|
756
|
-
}
|
|
757
|
-
|
|
1
|
+
const fs = require('fs').promises;
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const chalk = require('chalk');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Performance bottleneck analyzer for AIOS-FULLSTACK framework
|
|
7
|
+
* Identifies performance issues and optimization opportunities
|
|
8
|
+
*/
|
|
9
|
+
class PerformanceAnalyzer {
|
|
10
|
+
constructor(options = {}) {
|
|
11
|
+
this.rootPath = options.rootPath || process.cwd();
|
|
12
|
+
this.thresholds = {
|
|
13
|
+
fileSize: 50 * 1024, // 50KB
|
|
14
|
+
functionLength: 50, // lines
|
|
15
|
+
cyclomaticComplexity: 10,
|
|
16
|
+
nestingDepth: 5,
|
|
17
|
+
importCount: 20,
|
|
18
|
+
unusedCode: 0.1 // 10% unused code ratio
|
|
19
|
+
};
|
|
20
|
+
this.performancePatterns = {
|
|
21
|
+
// Sync operations that should be async
|
|
22
|
+
syncOperations: [
|
|
23
|
+
/fs\.readFileSync/g,
|
|
24
|
+
/fs\.writeFileSync/g,
|
|
25
|
+
/fs\.existsSync/g,
|
|
26
|
+
/JSON\.parse\(\s*fs\.readFileSync/g
|
|
27
|
+
],
|
|
28
|
+
// Memory-intensive patterns
|
|
29
|
+
memoryIssues: [
|
|
30
|
+
/\.map\(\s*.*\.map\(/g, // Nested maps
|
|
31
|
+
/new Array\(\d{4,}\)/g, // Large arrays
|
|
32
|
+
/JSON\.stringify.*JSON\.parse/g, // Unnecessary serialization
|
|
33
|
+
/require\(\s*['"`][^'"`]+\.json['"`]\s*\)/g // Large JSON imports
|
|
34
|
+
],
|
|
35
|
+
// Performance anti-patterns
|
|
36
|
+
antiPatterns: [
|
|
37
|
+
/console\.log/g, // Console logs in production
|
|
38
|
+
/debugger/g, // Debugger statements
|
|
39
|
+
/setTimeout\(\s*.*,\s*0\s*\)/g, // Unnecessary setTimeout
|
|
40
|
+
/setInterval\(\s*.*,\s*[0-9]{1,2}\s*\)/g // High-frequency intervals
|
|
41
|
+
],
|
|
42
|
+
// Inefficient loops
|
|
43
|
+
inefficientLoops: [
|
|
44
|
+
/for\s*\(\s*.*\.length\s*;/g, // Length calculated in loop condition
|
|
45
|
+
/while\s*\(\s*true\s*\)/g, // Infinite loops
|
|
46
|
+
/forEach.*forEach/g // Nested forEach
|
|
47
|
+
]
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Analyze performance across all components
|
|
53
|
+
*/
|
|
54
|
+
async analyzePerformance(components) {
|
|
55
|
+
const analysis = {
|
|
56
|
+
timestamp: new Date().toISOString(),
|
|
57
|
+
overall_score: 0,
|
|
58
|
+
bottlenecks: [],
|
|
59
|
+
optimization_opportunities: [],
|
|
60
|
+
memory_issues: [],
|
|
61
|
+
async_opportunities: [],
|
|
62
|
+
code_quality_issues: [],
|
|
63
|
+
file_size_issues: [],
|
|
64
|
+
complexity_issues: [],
|
|
65
|
+
recommendations: [],
|
|
66
|
+
metrics: {
|
|
67
|
+
total_files_analyzed: 0,
|
|
68
|
+
total_issues_found: 0,
|
|
69
|
+
critical_issues: 0,
|
|
70
|
+
high_priority_issues: 0,
|
|
71
|
+
medium_priority_issues: 0,
|
|
72
|
+
low_priority_issues: 0
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
try {
|
|
77
|
+
console.log(chalk.blue('🔍 Analyzing performance bottlenecks...'));
|
|
78
|
+
|
|
79
|
+
// Analyze each component
|
|
80
|
+
for (const component of components) {
|
|
81
|
+
const componentAnalysis = await this.analyzeComponentPerformance(component);
|
|
82
|
+
|
|
83
|
+
// Aggregate results
|
|
84
|
+
analysis.bottlenecks.push(...componentAnalysis.bottlenecks);
|
|
85
|
+
analysis.optimization_opportunities.push(...componentAnalysis.optimizations);
|
|
86
|
+
analysis.memory_issues.push(...componentAnalysis.memory_issues);
|
|
87
|
+
analysis.async_opportunities.push(...componentAnalysis.async_opportunities);
|
|
88
|
+
analysis.code_quality_issues.push(...componentAnalysis.quality_issues);
|
|
89
|
+
analysis.file_size_issues.push(...componentAnalysis.file_size_issues);
|
|
90
|
+
analysis.complexity_issues.push(...componentAnalysis.complexity_issues);
|
|
91
|
+
|
|
92
|
+
analysis.metrics.total_files_analyzed++;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Calculate overall metrics
|
|
96
|
+
analysis.overall_score = this.calculateOverallScore(analysis);
|
|
97
|
+
analysis.metrics.total_issues_found = this.countTotalIssues(analysis);
|
|
98
|
+
analysis.metrics = this.calculateIssuePriorities(analysis);
|
|
99
|
+
analysis.recommendations = this.generatePerformanceRecommendations(analysis);
|
|
100
|
+
|
|
101
|
+
console.log(chalk.green(`✅ Performance analysis completed`));
|
|
102
|
+
console.log(chalk.gray(` Files analyzed: ${analysis.metrics.total_files_analyzed}`));
|
|
103
|
+
console.log(chalk.gray(` Issues found: ${analysis.metrics.total_issues_found}`));
|
|
104
|
+
console.log(chalk.gray(` Overall score: ${analysis.overall_score}/10`));
|
|
105
|
+
|
|
106
|
+
return analysis;
|
|
107
|
+
|
|
108
|
+
} catch (error) {
|
|
109
|
+
console.error(chalk.red(`Performance analysis failed: ${error.message}`));
|
|
110
|
+
throw error;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Analyze performance for a single component
|
|
116
|
+
*/
|
|
117
|
+
async analyzeComponentPerformance(component) {
|
|
118
|
+
const analysis = {
|
|
119
|
+
component_id: component.id,
|
|
120
|
+
component_type: component.type,
|
|
121
|
+
bottlenecks: [],
|
|
122
|
+
optimizations: [],
|
|
123
|
+
memory_issues: [],
|
|
124
|
+
async_opportunities: [],
|
|
125
|
+
quality_issues: [],
|
|
126
|
+
file_size_issues: [],
|
|
127
|
+
complexity_issues: []
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
try {
|
|
131
|
+
// Only analyze JavaScript files and executable components
|
|
132
|
+
if (!this.shouldAnalyzeComponent(component)) {
|
|
133
|
+
return analysis;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const filePath = path.join(this.rootPath, component.file_path);
|
|
137
|
+
const content = await fs.readFile(filePath, 'utf-8');
|
|
138
|
+
const stats = await fs.stat(filePath);
|
|
139
|
+
|
|
140
|
+
// File size analysis
|
|
141
|
+
if (stats.size > this.thresholds.fileSize) {
|
|
142
|
+
analysis.file_size_issues.push({
|
|
143
|
+
component: component.id,
|
|
144
|
+
issue: `Large file size: ${this.formatBytes(stats.size)}`,
|
|
145
|
+
severity: stats.size > this.thresholds.fileSize * 2 ? 'high' : 'medium',
|
|
146
|
+
recommendation: 'Consider breaking down into smaller modules',
|
|
147
|
+
impact: 'high',
|
|
148
|
+
effort: 'medium'
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Synchronous operations analysis
|
|
153
|
+
const syncIssues = this.findSyncOperations(content, component);
|
|
154
|
+
analysis.async_opportunities.push(...syncIssues);
|
|
155
|
+
|
|
156
|
+
// Memory usage analysis
|
|
157
|
+
const memoryIssues = this.findMemoryIssues(content, component);
|
|
158
|
+
analysis.memory_issues.push(...memoryIssues);
|
|
159
|
+
|
|
160
|
+
// Performance anti-patterns
|
|
161
|
+
const antiPatterns = this.findAntiPatterns(content, component);
|
|
162
|
+
analysis.quality_issues.push(...antiPatterns);
|
|
163
|
+
|
|
164
|
+
// Loop efficiency analysis
|
|
165
|
+
const loopIssues = this.findLoopIssues(content, component);
|
|
166
|
+
analysis.bottlenecks.push(...loopIssues);
|
|
167
|
+
|
|
168
|
+
// Function complexity analysis
|
|
169
|
+
const complexityIssues = this.analyzeComplexity(content, component);
|
|
170
|
+
analysis.complexity_issues.push(...complexityIssues);
|
|
171
|
+
|
|
172
|
+
// Import/require analysis
|
|
173
|
+
const importIssues = this.analyzeImports(content, component);
|
|
174
|
+
analysis.optimizations.push(...importIssues);
|
|
175
|
+
|
|
176
|
+
// Dead code analysis
|
|
177
|
+
const deadCodeIssues = await this.findDeadCode(content, component);
|
|
178
|
+
analysis.optimizations.push(...deadCodeIssues);
|
|
179
|
+
|
|
180
|
+
} catch (error) {
|
|
181
|
+
console.warn(chalk.yellow(`Failed to analyze performance for ${component.id}: ${error.message}`));
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
return analysis;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Find synchronous operations that should be async
|
|
189
|
+
*/
|
|
190
|
+
findSyncOperations(content, component) {
|
|
191
|
+
const issues = [];
|
|
192
|
+
const lines = content.split('\n');
|
|
193
|
+
|
|
194
|
+
this.performancePatterns.syncOperations.forEach(pattern => {
|
|
195
|
+
lines.forEach((line, index) => {
|
|
196
|
+
const matches = line.match(pattern);
|
|
197
|
+
if (matches) {
|
|
198
|
+
matches.forEach(match => {
|
|
199
|
+
issues.push({
|
|
200
|
+
component: component.id,
|
|
201
|
+
issue: `Synchronous operation: ${match}`,
|
|
202
|
+
line: index + 1,
|
|
203
|
+
context: line.trim(),
|
|
204
|
+
severity: 'high',
|
|
205
|
+
recommendation: 'Convert to async equivalent',
|
|
206
|
+
impact: 'high',
|
|
207
|
+
effort: 'low'
|
|
208
|
+
});
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
});
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
return issues;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Find memory-intensive patterns
|
|
219
|
+
*/
|
|
220
|
+
findMemoryIssues(content, component) {
|
|
221
|
+
const issues = [];
|
|
222
|
+
const lines = content.split('\n');
|
|
223
|
+
|
|
224
|
+
this.performancePatterns.memoryIssues.forEach(pattern => {
|
|
225
|
+
lines.forEach((line, index) => {
|
|
226
|
+
const matches = line.match(pattern);
|
|
227
|
+
if (matches) {
|
|
228
|
+
matches.forEach(match => {
|
|
229
|
+
let severity = 'medium';
|
|
230
|
+
let recommendation = 'Optimize memory usage';
|
|
231
|
+
|
|
232
|
+
if (match.includes('new Array')) {
|
|
233
|
+
severity = 'high';
|
|
234
|
+
recommendation = 'Use array literals or streaming for large datasets';
|
|
235
|
+
} else if (match.includes('JSON.stringify.*JSON.parse')) {
|
|
236
|
+
recommendation = 'Use direct object assignment or deep clone utilities';
|
|
237
|
+
} else if (match.includes('.map(.*map(')) {
|
|
238
|
+
recommendation = 'Combine operations or use more efficient iteration';
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
issues.push({
|
|
242
|
+
component: component.id,
|
|
243
|
+
issue: `Memory-intensive pattern: ${match}`,
|
|
244
|
+
line: index + 1,
|
|
245
|
+
context: line.trim(),
|
|
246
|
+
severity,
|
|
247
|
+
recommendation,
|
|
248
|
+
impact: 'medium',
|
|
249
|
+
effort: 'medium'
|
|
250
|
+
});
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
});
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
return issues;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Find performance anti-patterns
|
|
261
|
+
*/
|
|
262
|
+
findAntiPatterns(content, component) {
|
|
263
|
+
const issues = [];
|
|
264
|
+
const lines = content.split('\n');
|
|
265
|
+
|
|
266
|
+
this.performancePatterns.antiPatterns.forEach(pattern => {
|
|
267
|
+
lines.forEach((line, index) => {
|
|
268
|
+
const matches = line.match(pattern);
|
|
269
|
+
if (matches) {
|
|
270
|
+
matches.forEach(match => {
|
|
271
|
+
let severity = 'low';
|
|
272
|
+
let recommendation = 'Remove or optimize';
|
|
273
|
+
|
|
274
|
+
if (match.includes('console.log')) {
|
|
275
|
+
recommendation = 'Remove console.log statements from production code';
|
|
276
|
+
} else if (match.includes('debugger')) {
|
|
277
|
+
severity = 'medium';
|
|
278
|
+
recommendation = 'Remove debugger statements';
|
|
279
|
+
} else if (match.includes('setTimeout')) {
|
|
280
|
+
recommendation = 'Use proper async/await or promises instead';
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
issues.push({
|
|
284
|
+
component: component.id,
|
|
285
|
+
issue: `Performance anti-pattern: ${match}`,
|
|
286
|
+
line: index + 1,
|
|
287
|
+
context: line.trim(),
|
|
288
|
+
severity,
|
|
289
|
+
recommendation,
|
|
290
|
+
impact: 'low',
|
|
291
|
+
effort: 'low'
|
|
292
|
+
});
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
});
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
return issues;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* Find inefficient loop patterns
|
|
303
|
+
*/
|
|
304
|
+
findLoopIssues(content, component) {
|
|
305
|
+
const issues = [];
|
|
306
|
+
const lines = content.split('\n');
|
|
307
|
+
|
|
308
|
+
this.performancePatterns.inefficientLoops.forEach(pattern => {
|
|
309
|
+
lines.forEach((line, index) => {
|
|
310
|
+
const matches = line.match(pattern);
|
|
311
|
+
if (matches) {
|
|
312
|
+
matches.forEach(match => {
|
|
313
|
+
let severity = 'medium';
|
|
314
|
+
let recommendation = 'Optimize loop structure';
|
|
315
|
+
|
|
316
|
+
if (match.includes('.length')) {
|
|
317
|
+
recommendation = 'Cache array length in variable before loop';
|
|
318
|
+
} else if (match.includes('while(true)')) {
|
|
319
|
+
severity = 'high';
|
|
320
|
+
recommendation = 'Add proper exit condition to prevent infinite loops';
|
|
321
|
+
} else if (match.includes('forEach.*forEach')) {
|
|
322
|
+
recommendation = 'Consider using nested for loops or array methods like flatMap';
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
issues.push({
|
|
326
|
+
component: component.id,
|
|
327
|
+
issue: `Inefficient loop: ${match}`,
|
|
328
|
+
line: index + 1,
|
|
329
|
+
context: line.trim(),
|
|
330
|
+
severity,
|
|
331
|
+
recommendation,
|
|
332
|
+
impact: 'medium',
|
|
333
|
+
effort: 'low'
|
|
334
|
+
});
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
});
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
return issues;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* Analyze function complexity
|
|
345
|
+
*/
|
|
346
|
+
analyzeComplexity(content, component) {
|
|
347
|
+
const issues = [];
|
|
348
|
+
const functions = this.extractFunctions(content);
|
|
349
|
+
|
|
350
|
+
functions.forEach(func => {
|
|
351
|
+
// Check function length
|
|
352
|
+
if (func.lines > this.thresholds.functionLength) {
|
|
353
|
+
issues.push({
|
|
354
|
+
component: component.id,
|
|
355
|
+
issue: `Long function: ${func.name} (${func.lines} lines)`,
|
|
356
|
+
line: func.startLine,
|
|
357
|
+
severity: func.lines > this.thresholds.functionLength * 2 ? 'high' : 'medium',
|
|
358
|
+
recommendation: 'Break down into smaller functions',
|
|
359
|
+
impact: 'medium',
|
|
360
|
+
effort: 'medium'
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// Check cyclomatic complexity
|
|
365
|
+
if (func.complexity > this.thresholds.cyclomaticComplexity) {
|
|
366
|
+
issues.push({
|
|
367
|
+
component: component.id,
|
|
368
|
+
issue: `High complexity: ${func.name} (complexity: ${func.complexity})`,
|
|
369
|
+
line: func.startLine,
|
|
370
|
+
severity: func.complexity > this.thresholds.cyclomaticComplexity * 1.5 ? 'high' : 'medium',
|
|
371
|
+
recommendation: 'Reduce conditional complexity',
|
|
372
|
+
impact: 'high',
|
|
373
|
+
effort: 'high'
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
// Check nesting depth
|
|
378
|
+
if (func.nestingDepth > this.thresholds.nestingDepth) {
|
|
379
|
+
issues.push({
|
|
380
|
+
component: component.id,
|
|
381
|
+
issue: `Deep nesting: ${func.name} (depth: ${func.nestingDepth})`,
|
|
382
|
+
line: func.startLine,
|
|
383
|
+
severity: 'medium',
|
|
384
|
+
recommendation: 'Reduce nesting depth using early returns or guard clauses',
|
|
385
|
+
impact: 'medium',
|
|
386
|
+
effort: 'medium'
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
return issues;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
/**
|
|
395
|
+
* Analyze imports and requires
|
|
396
|
+
*/
|
|
397
|
+
analyzeImports(content, component) {
|
|
398
|
+
const issues = [];
|
|
399
|
+
const imports = this.extractImports(content);
|
|
400
|
+
|
|
401
|
+
if (imports.length > this.thresholds.importCount) {
|
|
402
|
+
issues.push({
|
|
403
|
+
component: component.id,
|
|
404
|
+
issue: `Too many imports: ${imports.length}`,
|
|
405
|
+
severity: 'medium',
|
|
406
|
+
recommendation: 'Consider breaking down module or using dynamic imports',
|
|
407
|
+
impact: 'low',
|
|
408
|
+
effort: 'medium'
|
|
409
|
+
});
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
// Find unused imports (basic heuristic)
|
|
413
|
+
const unusedImports = imports.filter(imp => {
|
|
414
|
+
const usage = content.split(imp.name).length - 1;
|
|
415
|
+
return usage <= 1; // Only appears in import statement
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
if (unusedImports.length > 0) {
|
|
419
|
+
issues.push({
|
|
420
|
+
component: component.id,
|
|
421
|
+
issue: `Unused imports: ${unusedImports.map(i => i.name).join(', ')}`,
|
|
422
|
+
severity: 'low',
|
|
423
|
+
recommendation: 'Remove unused imports',
|
|
424
|
+
impact: 'low',
|
|
425
|
+
effort: 'low'
|
|
426
|
+
});
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
return issues;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
/**
|
|
433
|
+
* Find dead code (unused functions, variables)
|
|
434
|
+
*/
|
|
435
|
+
async findDeadCode(content, component) {
|
|
436
|
+
const issues = [];
|
|
437
|
+
|
|
438
|
+
// This is a simplified implementation
|
|
439
|
+
// A full implementation would analyze the entire codebase for usage
|
|
440
|
+
const functions = this.extractFunctions(content);
|
|
441
|
+
const exports = this.extractExports(content);
|
|
442
|
+
|
|
443
|
+
// Check for functions that are defined but never called within the file
|
|
444
|
+
functions.forEach(func => {
|
|
445
|
+
if (!func.name.startsWith('_') && !exports.includes(func.name)) {
|
|
446
|
+
const usage = content.split(func.name).length - 1;
|
|
447
|
+
if (usage <= 1) { // Only appears in definition
|
|
448
|
+
issues.push({
|
|
449
|
+
component: component.id,
|
|
450
|
+
issue: `Potentially unused function: ${func.name}`,
|
|
451
|
+
line: func.startLine,
|
|
452
|
+
severity: 'low',
|
|
453
|
+
recommendation: 'Remove if truly unused or export if needed elsewhere',
|
|
454
|
+
impact: 'low',
|
|
455
|
+
effort: 'low'
|
|
456
|
+
});
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
});
|
|
460
|
+
|
|
461
|
+
return issues;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
/**
|
|
465
|
+
* Extract function information from code
|
|
466
|
+
*/
|
|
467
|
+
extractFunctions(content) {
|
|
468
|
+
const functions = [];
|
|
469
|
+
const lines = content.split('\n');
|
|
470
|
+
const functionRegex = /(?:function\s+(\w+)|(\w+)\s*[:=]\s*(?:async\s+)?function|(?:async\s+)?(\w+)\s*\([^)]*\)\s*(?:=>|{))/g;
|
|
471
|
+
|
|
472
|
+
lines.forEach((line, index) => {
|
|
473
|
+
const matches = [...line.matchAll(functionRegex)];
|
|
474
|
+
matches.forEach(match => {
|
|
475
|
+
const name = match[1] || match[2] || match[3] || 'anonymous';
|
|
476
|
+
|
|
477
|
+
// Calculate function metrics (simplified)
|
|
478
|
+
const funcInfo = {
|
|
479
|
+
name,
|
|
480
|
+
startLine: index + 1,
|
|
481
|
+
lines: this.estimateFunctionLength(lines, index),
|
|
482
|
+
complexity: this.estimateComplexity(lines, index),
|
|
483
|
+
nestingDepth: this.estimateNestingDepth(lines, index)
|
|
484
|
+
};
|
|
485
|
+
|
|
486
|
+
functions.push(funcInfo);
|
|
487
|
+
});
|
|
488
|
+
});
|
|
489
|
+
|
|
490
|
+
return functions;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
/**
|
|
494
|
+
* Extract import information
|
|
495
|
+
*/
|
|
496
|
+
extractImports(content) {
|
|
497
|
+
const imports = [];
|
|
498
|
+
const importRegex = /(?:import\s+(?:(\w+)|\{([^}]+)\}|.*)\s+from\s+['"`]([^'"`]+)['"`]|const\s+(?:(\w+)|\{([^}]+)\})\s*=\s*require\(['"`]([^'"`]+)['"`]\))/g;
|
|
499
|
+
|
|
500
|
+
const matches = [...content.matchAll(importRegex)];
|
|
501
|
+
matches.forEach(match => {
|
|
502
|
+
const defaultImport = match[1] || match[4];
|
|
503
|
+
const namedImports = match[2] || match[5];
|
|
504
|
+
const source = match[3] || match[6];
|
|
505
|
+
|
|
506
|
+
if (defaultImport) {
|
|
507
|
+
imports.push({ name: defaultImport, source, type: 'default' });
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
if (namedImports) {
|
|
511
|
+
namedImports.split(',').forEach(name => {
|
|
512
|
+
imports.push({ name: name.trim(), source, type: 'named' });
|
|
513
|
+
});
|
|
514
|
+
}
|
|
515
|
+
});
|
|
516
|
+
|
|
517
|
+
return imports;
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
/**
|
|
521
|
+
* Extract export information
|
|
522
|
+
*/
|
|
523
|
+
extractExports(content) {
|
|
524
|
+
const exports = [];
|
|
525
|
+
const exportRegex = /(?:export\s+(?:default\s+)?(?:function\s+(\w+)|class\s+(\w+)|(?:const|let|var)\s+(\w+))|module\.exports\s*=\s*(\w+))/g;
|
|
526
|
+
|
|
527
|
+
const matches = [...content.matchAll(exportRegex)];
|
|
528
|
+
matches.forEach(match => {
|
|
529
|
+
const name = match[1] || match[2] || match[3] || match[4];
|
|
530
|
+
if (name) exports.push(name);
|
|
531
|
+
});
|
|
532
|
+
|
|
533
|
+
return exports;
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
/**
|
|
537
|
+
* Estimate function length (simplified)
|
|
538
|
+
*/
|
|
539
|
+
estimateFunctionLength(lines, startIndex) {
|
|
540
|
+
let braceCount = 0;
|
|
541
|
+
let lineCount = 0;
|
|
542
|
+
let inFunction = false;
|
|
543
|
+
|
|
544
|
+
for (let i = startIndex; i < lines.length; i++) {
|
|
545
|
+
const line = lines[i];
|
|
546
|
+
lineCount++;
|
|
547
|
+
|
|
548
|
+
// Count braces to find function end
|
|
549
|
+
for (const char of line) {
|
|
550
|
+
if (char === '{') {
|
|
551
|
+
braceCount++;
|
|
552
|
+
inFunction = true;
|
|
553
|
+
} else if (char === '}') {
|
|
554
|
+
braceCount--;
|
|
555
|
+
if (inFunction && braceCount === 0) {
|
|
556
|
+
return lineCount;
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
// Safety limit
|
|
562
|
+
if (lineCount > 200) break;
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
return lineCount;
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
/**
|
|
569
|
+
* Estimate cyclomatic complexity (simplified)
|
|
570
|
+
*/
|
|
571
|
+
estimateComplexity(lines, startIndex) {
|
|
572
|
+
let complexity = 1; // Base complexity
|
|
573
|
+
const complexityKeywords = ['if', 'else', 'while', 'for', 'switch', 'case', 'catch', '&&', '||', '?'];
|
|
574
|
+
|
|
575
|
+
const functionLines = lines.slice(startIndex, startIndex + this.estimateFunctionLength(lines, startIndex));
|
|
576
|
+
|
|
577
|
+
functionLines.forEach(line => {
|
|
578
|
+
complexityKeywords.forEach(keyword => {
|
|
579
|
+
const matches = line.match(new RegExp(`\\b${keyword}\\b`, 'g'));
|
|
580
|
+
if (matches) {
|
|
581
|
+
complexity += matches.length;
|
|
582
|
+
}
|
|
583
|
+
});
|
|
584
|
+
});
|
|
585
|
+
|
|
586
|
+
return complexity;
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
/**
|
|
590
|
+
* Estimate nesting depth (simplified)
|
|
591
|
+
*/
|
|
592
|
+
estimateNestingDepth(lines, startIndex) {
|
|
593
|
+
let maxDepth = 0;
|
|
594
|
+
let currentDepth = 0;
|
|
595
|
+
|
|
596
|
+
const functionLines = lines.slice(startIndex, startIndex + this.estimateFunctionLength(lines, startIndex));
|
|
597
|
+
|
|
598
|
+
functionLines.forEach(line => {
|
|
599
|
+
for (const char of line) {
|
|
600
|
+
if (char === '{') {
|
|
601
|
+
currentDepth++;
|
|
602
|
+
maxDepth = Math.max(maxDepth, currentDepth);
|
|
603
|
+
} else if (char === '}') {
|
|
604
|
+
currentDepth--;
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
});
|
|
608
|
+
|
|
609
|
+
return maxDepth;
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
/**
|
|
613
|
+
* Calculate overall performance score
|
|
614
|
+
*/
|
|
615
|
+
calculateOverallScore(analysis) {
|
|
616
|
+
let score = 10;
|
|
617
|
+
const penalties = {
|
|
618
|
+
critical: 2,
|
|
619
|
+
high: 1,
|
|
620
|
+
medium: 0.5,
|
|
621
|
+
low: 0.1
|
|
622
|
+
};
|
|
623
|
+
|
|
624
|
+
// Apply penalties based on issue severity
|
|
625
|
+
[...analysis.bottlenecks, ...analysis.memory_issues, ...analysis.async_opportunities,
|
|
626
|
+
...analysis.code_quality_issues, ...analysis.file_size_issues, ...analysis.complexity_issues]
|
|
627
|
+
.forEach(issue => {
|
|
628
|
+
score -= penalties[issue.severity] || 0.1;
|
|
629
|
+
});
|
|
630
|
+
|
|
631
|
+
return Math.max(0, Math.round(score * 10) / 10);
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
/**
|
|
635
|
+
* Count total issues
|
|
636
|
+
*/
|
|
637
|
+
countTotalIssues(analysis) {
|
|
638
|
+
return analysis.bottlenecks.length +
|
|
639
|
+
analysis.memory_issues.length +
|
|
640
|
+
analysis.async_opportunities.length +
|
|
641
|
+
analysis.code_quality_issues.length +
|
|
642
|
+
analysis.file_size_issues.length +
|
|
643
|
+
analysis.complexity_issues.length;
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
/**
|
|
647
|
+
* Calculate issue priorities
|
|
648
|
+
*/
|
|
649
|
+
calculateIssuePriorities(analysis) {
|
|
650
|
+
const metrics = analysis.metrics;
|
|
651
|
+
const allIssues = [
|
|
652
|
+
...analysis.bottlenecks,
|
|
653
|
+
...analysis.memory_issues,
|
|
654
|
+
...analysis.async_opportunities,
|
|
655
|
+
...analysis.code_quality_issues,
|
|
656
|
+
...analysis.file_size_issues,
|
|
657
|
+
...analysis.complexity_issues
|
|
658
|
+
];
|
|
659
|
+
|
|
660
|
+
metrics.critical_issues = allIssues.filter(i => i.severity === 'critical').length;
|
|
661
|
+
metrics.high_priority_issues = allIssues.filter(i => i.severity === 'high').length;
|
|
662
|
+
metrics.medium_priority_issues = allIssues.filter(i => i.severity === 'medium').length;
|
|
663
|
+
metrics.low_priority_issues = allIssues.filter(i => i.severity === 'low').length;
|
|
664
|
+
|
|
665
|
+
return metrics;
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
/**
|
|
669
|
+
* Generate performance recommendations
|
|
670
|
+
*/
|
|
671
|
+
generatePerformanceRecommendations(analysis) {
|
|
672
|
+
const recommendations = [];
|
|
673
|
+
|
|
674
|
+
// Critical issues
|
|
675
|
+
if (analysis.metrics.critical_issues > 0) {
|
|
676
|
+
recommendations.push({
|
|
677
|
+
priority: 'critical',
|
|
678
|
+
category: 'immediate_action',
|
|
679
|
+
message: `${analysis.metrics.critical_issues} critical performance issues require immediate attention`,
|
|
680
|
+
action: 'Fix critical bottlenecks before deployment'
|
|
681
|
+
});
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
// High severity issues
|
|
685
|
+
if (analysis.metrics.high_priority_issues > 5) {
|
|
686
|
+
recommendations.push({
|
|
687
|
+
priority: 'high',
|
|
688
|
+
category: 'performance',
|
|
689
|
+
message: `${analysis.metrics.high_priority_issues} high-priority performance issues detected`,
|
|
690
|
+
action: 'Address synchronous operations and complexity issues'
|
|
691
|
+
});
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
// Memory issues
|
|
695
|
+
if (analysis.memory_issues.length > 0) {
|
|
696
|
+
recommendations.push({
|
|
697
|
+
priority: 'medium',
|
|
698
|
+
category: 'memory',
|
|
699
|
+
message: `${analysis.memory_issues.length} memory optimization opportunities found`,
|
|
700
|
+
action: 'Implement memory-efficient patterns and data structures'
|
|
701
|
+
});
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
// File size issues
|
|
705
|
+
if (analysis.file_size_issues.length > 0) {
|
|
706
|
+
recommendations.push({
|
|
707
|
+
priority: 'medium',
|
|
708
|
+
category: 'modularity',
|
|
709
|
+
message: `${analysis.file_size_issues.length} files are too large`,
|
|
710
|
+
action: 'Break down large files into smaller, focused modules'
|
|
711
|
+
});
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
// Overall score
|
|
715
|
+
if (analysis.overall_score < 7) {
|
|
716
|
+
recommendations.push({
|
|
717
|
+
priority: 'high',
|
|
718
|
+
category: 'general',
|
|
719
|
+
message: `Overall performance score is ${analysis.overall_score}/10`,
|
|
720
|
+
action: 'Comprehensive performance review and optimization needed'
|
|
721
|
+
});
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
return recommendations;
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
/**
|
|
728
|
+
* Check if component should be analyzed
|
|
729
|
+
*/
|
|
730
|
+
shouldAnalyzeComponent(component) {
|
|
731
|
+
const analyzableTypes = ['utility', 'task'];
|
|
732
|
+
const analyzableExtensions = ['.js', '.mjs', '.ts'];
|
|
733
|
+
|
|
734
|
+
if (!analyzableTypes.includes(component.type)) {
|
|
735
|
+
return false;
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
if (component.file_path) {
|
|
739
|
+
const ext = path.extname(component.file_path).toLowerCase();
|
|
740
|
+
return analyzableExtensions.includes(ext);
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
return false;
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
/**
|
|
747
|
+
* Format bytes for display
|
|
748
|
+
*/
|
|
749
|
+
formatBytes(bytes) {
|
|
750
|
+
if (bytes === 0) return '0 B';
|
|
751
|
+
const k = 1024;
|
|
752
|
+
const sizes = ['B', 'KB', 'MB', 'GB'];
|
|
753
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
754
|
+
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
|
|
758
758
|
module.exports = PerformanceAnalyzer;
|