performance-optimizer 2.5.0__py3-none-any.whl
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.
- analyzers/__init__.py +0 -0
- analyzers/angular_analyzer.py +406 -0
- analyzers/aws_analyzer.py +122 -0
- analyzers/node_analyzer.py +306 -0
- analyzers/react_analyzer.py +214 -0
- analyzers/sql_analyzer.py +184 -0
- core/__init__.py +0 -0
- core/issue.py +67 -0
- core/scorer.py +94 -0
- optimizer.py +339 -0
- performance_optimizer-2.5.0.dist-info/METADATA +236 -0
- performance_optimizer-2.5.0.dist-info/RECORD +18 -0
- performance_optimizer-2.5.0.dist-info/WHEEL +5 -0
- performance_optimizer-2.5.0.dist-info/entry_points.txt +3 -0
- performance_optimizer-2.5.0.dist-info/licenses/LICENSE +21 -0
- performance_optimizer-2.5.0.dist-info/top_level.txt +4 -0
- reporter/__init__.py +0 -0
- reporter/html_reporter.py +1502 -0
optimizer.py
ADDED
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Performance Optimizer v2.0 - Advanced Multi-Stack Analyzer
|
|
4
|
+
Angular • Node.js • Seneca • SQL • AWS
|
|
5
|
+
NOT SonarQube — Performance-Specific Only
|
|
6
|
+
|
|
7
|
+
Usage:
|
|
8
|
+
python optimizer.py --frontend /path/to/angular-repo
|
|
9
|
+
python optimizer.py --backend /path/to/node-repo
|
|
10
|
+
python optimizer.py --project /path/to/monorepo
|
|
11
|
+
python optimizer.py --frontend ./fe --backend ./be --fail-on critical --min-score 70
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
import argparse
|
|
15
|
+
import json
|
|
16
|
+
import os
|
|
17
|
+
import sys
|
|
18
|
+
import time
|
|
19
|
+
from datetime import datetime
|
|
20
|
+
from pathlib import Path
|
|
21
|
+
from typing import List, Set
|
|
22
|
+
|
|
23
|
+
sys.path.insert(0, os.path.dirname(__file__))
|
|
24
|
+
|
|
25
|
+
from analyzers.angular_analyzer import AngularAnalyzer
|
|
26
|
+
from analyzers.react_analyzer import ReactAnalyzer
|
|
27
|
+
from analyzers.node_analyzer import NodeAnalyzer
|
|
28
|
+
from analyzers.sql_analyzer import SqlAnalyzer
|
|
29
|
+
from analyzers.aws_analyzer import AwsAnalyzer
|
|
30
|
+
from core.scorer import Scorer
|
|
31
|
+
from core.issue import Severity, Issue
|
|
32
|
+
from reporter.html_reporter import HtmlReporter
|
|
33
|
+
|
|
34
|
+
BANNER = """
|
|
35
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
36
|
+
│ PERFORMANCE OPTIMIZER v2.5 PRO │
|
|
37
|
+
│ Enterprise Multi-Stack Performance Intelligence │
|
|
38
|
+
│ Angular • React • Node.js • SQL • AWS Cloud │
|
|
39
|
+
│ NOT SonarQube — Performance-Specific Only │
|
|
40
|
+
└─────────────────────────────────────────────────────────────┘
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
DEFAULT_EXCLUDES = [
|
|
44
|
+
'node_modules', '.git', 'dist', 'build', '.angular', '.next',
|
|
45
|
+
'coverage', '.vscode', '.idea', 'package-lock.json', 'yarn.lock'
|
|
46
|
+
]
|
|
47
|
+
|
|
48
|
+
def should_exclude(path_str: str, custom_excludes: List[str]) -> bool:
|
|
49
|
+
normalized = path_str.replace('\\', '/')
|
|
50
|
+
all_excludes = DEFAULT_EXCLUDES + custom_excludes
|
|
51
|
+
return any(ex in normalized for ex in all_excludes if ex)
|
|
52
|
+
|
|
53
|
+
def count_files(path: str, custom_excludes: List[str]) -> int:
|
|
54
|
+
p = Path(path)
|
|
55
|
+
count = 0
|
|
56
|
+
extensions = ['*.ts', '*.js', '*.jsx', '*.tsx', '*.html', '*.sql', '*.json', '*.yml', '*.yaml', '*.tf']
|
|
57
|
+
for ext in extensions:
|
|
58
|
+
for f in p.rglob(ext):
|
|
59
|
+
if not should_exclude(str(f), custom_excludes):
|
|
60
|
+
count += 1
|
|
61
|
+
return count
|
|
62
|
+
|
|
63
|
+
def detect_tech(frontend_path: str, backend_path: str) -> str:
|
|
64
|
+
techs = []
|
|
65
|
+
checked_paths = set(filter(None, [frontend_path, backend_path]))
|
|
66
|
+
for path in checked_paths:
|
|
67
|
+
p = Path(path)
|
|
68
|
+
pkg = p / 'package.json'
|
|
69
|
+
if pkg.exists():
|
|
70
|
+
try:
|
|
71
|
+
data = json.loads(pkg.read_text(encoding='utf-8', errors='ignore'))
|
|
72
|
+
deps = {**data.get('dependencies', {}), **data.get('devDependencies', {})}
|
|
73
|
+
if '@angular/core' in deps:
|
|
74
|
+
ver = deps['@angular/core'].lstrip('^~')
|
|
75
|
+
techs.append(f'Angular {ver.split(".")[0]}')
|
|
76
|
+
if 'react' in deps:
|
|
77
|
+
ver = deps['react'].lstrip('^~')
|
|
78
|
+
techs.append(f'React {ver.split(".")[0]}')
|
|
79
|
+
if 'next' in deps:
|
|
80
|
+
techs.append('Next.js')
|
|
81
|
+
if 'vue' in deps:
|
|
82
|
+
techs.append('Vue')
|
|
83
|
+
if 'seneca' in deps:
|
|
84
|
+
techs.append('Seneca.js')
|
|
85
|
+
if 'express' in deps:
|
|
86
|
+
techs.append('Express')
|
|
87
|
+
if 'nest' in deps or '@nestjs/core' in deps:
|
|
88
|
+
techs.append('NestJS')
|
|
89
|
+
node_ver = data.get('engines', {}).get('node', '')
|
|
90
|
+
if node_ver:
|
|
91
|
+
techs.append(f'Node {node_ver}')
|
|
92
|
+
except Exception:
|
|
93
|
+
pass
|
|
94
|
+
|
|
95
|
+
# Check for AWS Infra
|
|
96
|
+
if any(p.rglob('serverless.yml')) or any(p.rglob('*.tf')) or any(p.rglob('cloudformation*.yml')):
|
|
97
|
+
if 'AWS' not in techs:
|
|
98
|
+
techs.append('AWS Cloud')
|
|
99
|
+
# Check for SQL files
|
|
100
|
+
if any(p.rglob('*.sql')):
|
|
101
|
+
if 'SQL/RDS' not in techs:
|
|
102
|
+
techs.append('SQL/RDS')
|
|
103
|
+
|
|
104
|
+
return ' • '.join(techs) if techs else 'Angular • React • Node.js • SQL • AWS'
|
|
105
|
+
|
|
106
|
+
def run_analyzers(frontend_path: str, backend_path: str, verbose: bool = False) -> List[Issue]:
|
|
107
|
+
all_issues = []
|
|
108
|
+
is_monorepo = (frontend_path and backend_path and Path(frontend_path).resolve() == Path(backend_path).resolve())
|
|
109
|
+
|
|
110
|
+
if is_monorepo or (frontend_path and not backend_path):
|
|
111
|
+
target = frontend_path
|
|
112
|
+
print(f' 🔍 Analyzing unified project: {target}')
|
|
113
|
+
if Path(target).exists():
|
|
114
|
+
print(' 🅰️ Running Angular Analyzer...')
|
|
115
|
+
all_issues.extend(AngularAnalyzer(target).analyze())
|
|
116
|
+
print(' ⚛️ Running React Analyzer...')
|
|
117
|
+
all_issues.extend(ReactAnalyzer(target).analyze())
|
|
118
|
+
print(' 🟢 Running Node.js & Seneca Analyzer...')
|
|
119
|
+
all_issues.extend(NodeAnalyzer(target).analyze())
|
|
120
|
+
print(' 🗄️ Running SQL & Database Analyzer...')
|
|
121
|
+
all_issues.extend(SqlAnalyzer(target).analyze())
|
|
122
|
+
print(' ☁️ Running AWS & Infrastructure Analyzer...')
|
|
123
|
+
all_issues.extend(AwsAnalyzer(target).analyze())
|
|
124
|
+
return all_issues
|
|
125
|
+
|
|
126
|
+
# Separate frontend and backend paths
|
|
127
|
+
if frontend_path:
|
|
128
|
+
fp = Path(frontend_path)
|
|
129
|
+
if fp.exists():
|
|
130
|
+
print(f' 🅰️ Analyzing Angular/Frontend: {frontend_path}')
|
|
131
|
+
issues = AngularAnalyzer(frontend_path).analyze()
|
|
132
|
+
all_issues.extend(issues)
|
|
133
|
+
print(f' Found {len(issues)} Angular performance issues')
|
|
134
|
+
|
|
135
|
+
print(f' ⚛️ Analyzing React/Frontend: {frontend_path}')
|
|
136
|
+
react_issues = ReactAnalyzer(frontend_path).analyze()
|
|
137
|
+
all_issues.extend(react_issues)
|
|
138
|
+
print(f' Found {len(react_issues)} React performance issues')
|
|
139
|
+
else:
|
|
140
|
+
print(f' ❌ Frontend path not found: {frontend_path}')
|
|
141
|
+
|
|
142
|
+
if backend_path:
|
|
143
|
+
bp = Path(backend_path)
|
|
144
|
+
if bp.exists():
|
|
145
|
+
print(f' 🟢 Analyzing Node.js/Seneca: {backend_path}')
|
|
146
|
+
node_issues = NodeAnalyzer(backend_path).analyze()
|
|
147
|
+
all_issues.extend(node_issues)
|
|
148
|
+
print(f' Found {len(node_issues)} backend performance issues')
|
|
149
|
+
|
|
150
|
+
print(f' 🗄️ Analyzing SQL/Database: {backend_path}')
|
|
151
|
+
sql_issues = SqlAnalyzer(backend_path).analyze()
|
|
152
|
+
all_issues.extend(sql_issues)
|
|
153
|
+
print(f' Found {len(sql_issues)} database performance issues')
|
|
154
|
+
|
|
155
|
+
print(f' ☁️ Analyzing AWS/Infra config: {backend_path}')
|
|
156
|
+
aws_issues = AwsAnalyzer(backend_path).analyze()
|
|
157
|
+
all_issues.extend(aws_issues)
|
|
158
|
+
print(f' Found {len(aws_issues)} infrastructure issues')
|
|
159
|
+
else:
|
|
160
|
+
print(f' ❌ Backend path not found: {backend_path}')
|
|
161
|
+
|
|
162
|
+
return all_issues
|
|
163
|
+
|
|
164
|
+
def print_summary(scores: dict, issues: List[Issue]):
|
|
165
|
+
s = scores
|
|
166
|
+
print(f'''
|
|
167
|
+
┌{'─'*58}┐
|
|
168
|
+
│ PERFORMANCE SCAN RESULTS │
|
|
169
|
+
├{'─'*58}┤
|
|
170
|
+
│ Overall Score : {s['overall']:>3}/100 {'(🔴 Critical)' if s['overall']<45 else '(⚠️ Needs Work)' if s['overall']<65 else '(🟡 Fair)' if s['overall']<80 else '(✅ Good)':16}{'':13}│
|
|
171
|
+
│ Frontend : {s['frontend']:>3}/100{'':36}│
|
|
172
|
+
│ Backend : {s['backend']:>3}/100{'':36}│
|
|
173
|
+
│ Database : {s['database']:>3}/100{'':36}│
|
|
174
|
+
│ Infra : {s['infra']:>3}/100{'':36}│
|
|
175
|
+
├{'─'*58}┤
|
|
176
|
+
│ 🔴 Critical: {s['by_severity']['critical']:<4} 🟠 High: {s['by_severity']['high']:<4} 🟡 Medium: {s['by_severity']['medium']:<4} 🟢 Low: {s['by_severity']['low']:<4} │
|
|
177
|
+
│ Total Issues : {s['total_issues']:>3}{'':42}│
|
|
178
|
+
└{'─'*58}┘''')
|
|
179
|
+
|
|
180
|
+
if s.get('top3'):
|
|
181
|
+
print('\n 🚨 TOP ACTIONABLE FIXES (Show to leadership):')
|
|
182
|
+
for idx, t in enumerate(s['top3'], 1):
|
|
183
|
+
line_str = f":{t['line_number']}" if t.get('line_number') else ""
|
|
184
|
+
print(f' {idx}. [{t["severity"].upper()}] {t["title"]}')
|
|
185
|
+
print(f' Location : {t["file"]}{line_str}')
|
|
186
|
+
print(f' Gain : {t["perf_gain"]}')
|
|
187
|
+
|
|
188
|
+
def generate_markdown_summary(out_path: str, scores: dict, meta: dict, issues: List[Issue]):
|
|
189
|
+
s = scores
|
|
190
|
+
md = f"""# Performance Optimizer Scan Summary
|
|
191
|
+
|
|
192
|
+
**Overall Health Score**: `{s['overall']}/100`
|
|
193
|
+
**Target Project**: `{meta.get('project', 'Application')}`
|
|
194
|
+
**Scan Timestamp**: `{meta.get('scan_time')}`
|
|
195
|
+
**Analyzed Files**: `{meta.get('files_scanned')}` in `{meta.get('duration')}s`
|
|
196
|
+
|
|
197
|
+
---
|
|
198
|
+
|
|
199
|
+
### 📊 Layer Breakdown
|
|
200
|
+
| Layer | Score | Status |
|
|
201
|
+
| :--- | :--- | :--- |
|
|
202
|
+
| **Frontend (Angular / React)** | `{s['frontend']}/100` | {'🔴 Critical' if s['frontend']<50 else '🟠 High Risk' if s['frontend']<70 else '✅ Good'} |
|
|
203
|
+
| **Node.js / Seneca** | `{s['backend']}/100` | {'🔴 Critical' if s['backend']<50 else '🟠 High Risk' if s['backend']<70 else '✅ Good'} |
|
|
204
|
+
| **SQL / Database** | `{s['database']}/100` | {'🔴 Critical' if s['database']<50 else '🟠 High Risk' if s['database']<70 else '✅ Good'} |
|
|
205
|
+
| **AWS / Cloud Infra** | `{s['infra']}/100` | {'🔴 Critical' if s['infra']<50 else '🟠 High Risk' if s['infra']<70 else '✅ Good'} |
|
|
206
|
+
|
|
207
|
+
### 🚨 Top Critical Bottlenecks
|
|
208
|
+
"""
|
|
209
|
+
for idx, t in enumerate(s.get('top3', []), 1):
|
|
210
|
+
md += f"\n{idx}. **[{t['severity'].upper()}] {t['title']}**\n"
|
|
211
|
+
md += f" - **File**: `{t['file']}`\n"
|
|
212
|
+
md += f" - **Impact**: {t['perf_gain']}\n"
|
|
213
|
+
|
|
214
|
+
md += "\n> 💡 *Generated by [Performance Optimizer v2.5 PRO](https://github.com/Ravik27280/Performance-Optimizer)*\n"
|
|
215
|
+
|
|
216
|
+
with open(out_path, 'w', encoding='utf-8') as fh:
|
|
217
|
+
fh.write(md)
|
|
218
|
+
print(f' 📝 Markdown summary saved: {out_path}')
|
|
219
|
+
|
|
220
|
+
def main():
|
|
221
|
+
print(BANNER)
|
|
222
|
+
|
|
223
|
+
parser = argparse.ArgumentParser(
|
|
224
|
+
description='Performance Optimizer v2.5 PRO - Enterprise Multi-Stack Performance Analyzer'
|
|
225
|
+
)
|
|
226
|
+
parser.add_argument('--frontend', '-f', help='Path to Angular frontend repo', default=None)
|
|
227
|
+
parser.add_argument('--backend', '-b', help='Path to Node.js/Seneca backend repo', default=None)
|
|
228
|
+
parser.add_argument('--project', '-p', help='Path to monorepo (scans entire workspace)', default=None)
|
|
229
|
+
parser.add_argument('--output', '-o', help='Output HTML report path', default='performance_report.html')
|
|
230
|
+
parser.add_argument('--json', '-j', help='Output machine-readable JSON report', action='store_true')
|
|
231
|
+
parser.add_argument('--markdown-summary', '-m', help='Generate markdown report summary for PR comments', default=None)
|
|
232
|
+
parser.add_argument('--exclude', '-e', help='Comma-separated directory patterns to exclude', default='')
|
|
233
|
+
parser.add_argument('--fail-on', help='Fail CI with exit 1 if issues matching severity exist (critical, high)', default=None)
|
|
234
|
+
parser.add_argument('--min-score', help='Fail CI with exit 1 if overall score is below threshold (0-100)', type=int, default=None)
|
|
235
|
+
parser.add_argument('--verbose', '-v', help='Verbose terminal output', action='store_true')
|
|
236
|
+
args = parser.parse_args()
|
|
237
|
+
|
|
238
|
+
frontend_path = args.frontend or args.project
|
|
239
|
+
backend_path = args.backend or args.project
|
|
240
|
+
custom_excludes = [x.strip() for x in args.exclude.split(',') if x.strip()]
|
|
241
|
+
|
|
242
|
+
if not frontend_path and not backend_path:
|
|
243
|
+
print(' ❌ ERROR: Provide at least one of --frontend, --backend, or --project')
|
|
244
|
+
print(' Example: python optimizer.py --frontend ./angular-app --backend ./node-api')
|
|
245
|
+
parser.print_help()
|
|
246
|
+
sys.exit(1)
|
|
247
|
+
|
|
248
|
+
print(f' 📁 Frontend path : {frontend_path or "(not provided)"}')
|
|
249
|
+
print(f' 📁 Backend path : {backend_path or "(not provided)"}')
|
|
250
|
+
print(f' 💾 HTML Output : {args.output}')
|
|
251
|
+
|
|
252
|
+
total_files = 0
|
|
253
|
+
if frontend_path and Path(frontend_path).exists():
|
|
254
|
+
total_files += count_files(frontend_path, custom_excludes)
|
|
255
|
+
if backend_path and Path(backend_path).exists() and Path(backend_path).resolve() != Path(frontend_path or '').resolve():
|
|
256
|
+
total_files += count_files(backend_path, custom_excludes)
|
|
257
|
+
|
|
258
|
+
tech = detect_tech(frontend_path, backend_path)
|
|
259
|
+
print(f' 🔎 Detected tech : {tech}')
|
|
260
|
+
print(f' 📁 Files to scan : {total_files}')
|
|
261
|
+
print('\n Running analyzers...')
|
|
262
|
+
|
|
263
|
+
start_time = time.time()
|
|
264
|
+
all_issues = run_analyzers(frontend_path, backend_path, verbose=args.verbose)
|
|
265
|
+
duration = round(time.time() - start_time, 2)
|
|
266
|
+
|
|
267
|
+
print(f'\n ✔ Scan completed in {duration}s')
|
|
268
|
+
|
|
269
|
+
# Deduplicate issues with same (id, file, line_number)
|
|
270
|
+
seen: Set[tuple] = set()
|
|
271
|
+
unique_issues = []
|
|
272
|
+
for issue in all_issues:
|
|
273
|
+
key = (issue.id, issue.file, issue.line_number)
|
|
274
|
+
if key not in seen:
|
|
275
|
+
seen.add(key)
|
|
276
|
+
unique_issues.append(issue)
|
|
277
|
+
all_issues = unique_issues
|
|
278
|
+
|
|
279
|
+
# Compute scores
|
|
280
|
+
scorer = Scorer()
|
|
281
|
+
scores = scorer.compute(all_issues)
|
|
282
|
+
print_summary(scores, all_issues)
|
|
283
|
+
|
|
284
|
+
# Render HTML Dashboard
|
|
285
|
+
scan_time = datetime.now().strftime('%d %b %Y, %I:%M %p')
|
|
286
|
+
project_name = Path(frontend_path or backend_path).name or "Performance Project"
|
|
287
|
+
meta = {
|
|
288
|
+
'project': project_name,
|
|
289
|
+
'scan_time': scan_time,
|
|
290
|
+
'files_scanned': total_files,
|
|
291
|
+
'duration': duration,
|
|
292
|
+
'tech': tech,
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
reporter = HtmlReporter(all_issues, scores, meta)
|
|
296
|
+
reporter.render(args.output)
|
|
297
|
+
print(f'\n 📊 Interactive HTML Dashboard saved: {args.output}')
|
|
298
|
+
|
|
299
|
+
# JSON export
|
|
300
|
+
if args.json:
|
|
301
|
+
json_path = args.output.replace('.html', '.json') if args.output.endswith('.html') else f"{args.output}.json"
|
|
302
|
+
report_data = {
|
|
303
|
+
'meta': meta,
|
|
304
|
+
'scores': scores,
|
|
305
|
+
'issues': [i.to_dict() for i in all_issues],
|
|
306
|
+
}
|
|
307
|
+
with open(json_path, 'w', encoding='utf-8') as jf:
|
|
308
|
+
json.dump(report_data, jf, indent=2)
|
|
309
|
+
print(f' 💾 Machine-readable JSON saved: {json_path}')
|
|
310
|
+
|
|
311
|
+
# Markdown summary
|
|
312
|
+
if args.markdown_summary:
|
|
313
|
+
generate_markdown_summary(args.markdown_summary, scores, meta, all_issues)
|
|
314
|
+
|
|
315
|
+
# CI/CD Quality Gate evaluation
|
|
316
|
+
exit_code = 0
|
|
317
|
+
if args.fail_on:
|
|
318
|
+
threshold_sev = args.fail_on.lower()
|
|
319
|
+
critical_count = scores['by_severity'].get('critical', 0)
|
|
320
|
+
high_count = scores['by_severity'].get('high', 0)
|
|
321
|
+
|
|
322
|
+
if threshold_sev == 'critical' and critical_count > 0:
|
|
323
|
+
print(f'\n ❌ CI QUALITY GATE FAILED: Found {critical_count} critical performance issues (--fail-on critical)')
|
|
324
|
+
exit_code = 1
|
|
325
|
+
elif threshold_sev == 'high' and (critical_count > 0 or high_count > 0):
|
|
326
|
+
print(f'\n ❌ CI QUALITY GATE FAILED: Found {critical_count} critical and {high_count} high issues (--fail-on high)')
|
|
327
|
+
exit_code = 1
|
|
328
|
+
|
|
329
|
+
if args.min_score is not None:
|
|
330
|
+
if scores['overall'] < args.min_score:
|
|
331
|
+
print(f'\n ❌ CI QUALITY GATE FAILED: Overall score {scores["overall"]} is below minimum threshold of {args.min_score} (--min-score {args.min_score})')
|
|
332
|
+
exit_code = 1
|
|
333
|
+
|
|
334
|
+
if exit_code == 0:
|
|
335
|
+
print('\n ✔ Done! All quality checks passed.\n')
|
|
336
|
+
sys.exit(exit_code)
|
|
337
|
+
|
|
338
|
+
if __name__ == '__main__':
|
|
339
|
+
main()
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: performance-optimizer
|
|
3
|
+
Version: 2.5.0
|
|
4
|
+
Summary: Ultra-fast zero-dependency static performance analyzer for Angular, React, Node.js, SQL, and AWS.
|
|
5
|
+
Author: Performance Optimizer Core Team
|
|
6
|
+
License: MIT
|
|
7
|
+
Keywords: performance,static-analysis,angular,react,nodejs,sql,aws,latency,optimizer
|
|
8
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
10
|
+
Classifier: Topic :: Software Development :: Quality Assurance
|
|
11
|
+
Classifier: Topic :: Software Development :: Bug Tracking
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.7
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Requires-Python: >=3.7
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
License-File: LICENSE
|
|
23
|
+
Dynamic: license-file
|
|
24
|
+
|
|
25
|
+
# Performance Optimizer v2.5 PRO — Advanced Multi-Stack Analyzer
|
|
26
|
+
|
|
27
|
+
> **NOT SonarQube.** This is an enterprise static performance & latency intelligence engine.
|
|
28
|
+
> **Targeted Runtimes**: Angular (14–19+), React (17–19+) & Next.js, Node.js & Seneca.js, SQL / Database Indexes, and AWS Serverless.
|
|
29
|
+
> Zero external dependencies — runs in `<0.05s` with Python 3.7+ standard library.
|
|
30
|
+
|
|
31
|
+
[](https://ravik27280.github.io/Performance-Optimizer/)
|
|
32
|
+
[](https://www.python.org/)
|
|
33
|
+
[](https://opensource.org/licenses/MIT)
|
|
34
|
+
|
|
35
|
+
🌐 **Live Interactive Web Portal & Diagnostic Scanner**: [https://ravik27280.github.io/Performance-Optimizer/](https://ravik27280.github.io/Performance-Optimizer/)
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## ⚡ Installation & CLI Setup
|
|
40
|
+
|
|
41
|
+
Install locally or as an editable package for global terminal access:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
# Clone the repository
|
|
45
|
+
git clone https://github.com/Ravik27280/Performance-Optimizer.git
|
|
46
|
+
cd Performance-Optimizer
|
|
47
|
+
|
|
48
|
+
# Install package globally / in virtual environment
|
|
49
|
+
pip install -e .
|
|
50
|
+
|
|
51
|
+
# Verify CLI installation
|
|
52
|
+
perf-optimizer --help
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## 🚀 Quick Start Examples
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
# 1. Scan current repository / monorepo
|
|
61
|
+
perf-optimizer --project .
|
|
62
|
+
|
|
63
|
+
# 2. Scan separate frontend and backend directories
|
|
64
|
+
perf-optimizer --frontend ./angular-app --backend ./node-api
|
|
65
|
+
|
|
66
|
+
# 3. Enforce CI/CD Quality Gate (Exit 1 on critical issues or score < 75)
|
|
67
|
+
perf-optimizer --project . --fail-on critical --min-score 75
|
|
68
|
+
|
|
69
|
+
# 4. Generate HTML dashboard, JSON audit, and PR Markdown summary
|
|
70
|
+
perf-optimizer --project . --json --markdown-summary pr_summary.md --output report.html
|
|
71
|
+
|
|
72
|
+
# 5. Alternatively, run directly with python (zero pip packages required)
|
|
73
|
+
python optimizer.py --project . --verbose
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## 📊 Output Formats
|
|
79
|
+
|
|
80
|
+
- **`performance_report.html`** — Interactive executive dashboard with code diff previews, ROI impact calculations, and dark/light modes.
|
|
81
|
+
- **`performance_report.json`** — Machine-readable audit data with exact line numbers and defect categories for custom tooling.
|
|
82
|
+
- **`pr_summary.md`** — Markdown report table designed for automated Pull Request comments in GitHub Actions and GitLab CI.
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## 🤖 GitHub Actions CI/CD Integration
|
|
87
|
+
|
|
88
|
+
Create `.github/workflows/perf-audit.yml` to automatically run audits on every Pull Request:
|
|
89
|
+
|
|
90
|
+
```yaml
|
|
91
|
+
name: Performance Quality Gate
|
|
92
|
+
|
|
93
|
+
on:
|
|
94
|
+
push:
|
|
95
|
+
branches: [ main ]
|
|
96
|
+
pull_request:
|
|
97
|
+
branches: [ main ]
|
|
98
|
+
|
|
99
|
+
jobs:
|
|
100
|
+
audit:
|
|
101
|
+
runs-on: ubuntu-latest
|
|
102
|
+
steps:
|
|
103
|
+
- uses: actions/checkout@v4
|
|
104
|
+
- uses: actions/setup-python@v5
|
|
105
|
+
with:
|
|
106
|
+
python-version: '3.11'
|
|
107
|
+
- name: Install Performance Optimizer
|
|
108
|
+
run: pip install -e .
|
|
109
|
+
- name: Run Performance Audit
|
|
110
|
+
run: |
|
|
111
|
+
perf-optimizer --project . --fail-on critical --min-score 75
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## 🛡️ CLI Flags & Options
|
|
117
|
+
|
|
118
|
+
| Argument | Description | Example |
|
|
119
|
+
|---|---|---|
|
|
120
|
+
| `--project`, `-p` | Path to monorepo or project (scans entire workspace) | `--project .` |
|
|
121
|
+
| `--frontend`, `-f` | Path to Angular or React frontend codebase | `--frontend ./src/web` |
|
|
122
|
+
| `--backend`, `-b` | Path to Node.js/Seneca/Express codebase | `--backend ./src/api` |
|
|
123
|
+
| `--fail-on` | Fails CI with exit code `1` if issues matching severity exist | `--fail-on critical` or `--fail-on high` |
|
|
124
|
+
| `--min-score` | Fails CI with exit code `1` if overall score is below threshold | `--min-score 75` |
|
|
125
|
+
| `--exclude`, `-e` | Comma-separated directory patterns to exclude | `--exclude "legacy,tmp,e2e"` |
|
|
126
|
+
| `--json`, `-j` | Generates machine-readable JSON report | `--json` |
|
|
127
|
+
| `--markdown-summary`, `-m` | Generates PR comment summary file | `--markdown-summary pr_summary.md` |
|
|
128
|
+
| `--output`, `-o` | Custom HTML report output path | `--output ./dist/perf_report.html` |
|
|
129
|
+
| `--verbose`, `-v` | Detailed terminal output during scan | `--verbose` |
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
## 🔇 Inline Rule Suppression
|
|
134
|
+
|
|
135
|
+
Suppress false positives or accepted trade-offs directly in code:
|
|
136
|
+
|
|
137
|
+
```typescript
|
|
138
|
+
// Angular / TypeScript / JavaScript
|
|
139
|
+
// perf-ignore ANG001
|
|
140
|
+
@Component({ ... })
|
|
141
|
+
|
|
142
|
+
// Node.js
|
|
143
|
+
// perf-ignore NODE001
|
|
144
|
+
const bootConfig = fs.readFileSync('boot.json', 'utf8');
|
|
145
|
+
|
|
146
|
+
// SQL / Schema
|
|
147
|
+
-- perf-ignore SQL004
|
|
148
|
+
CREATE TABLE temp_session ( ... );
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
## 🔍 What It Detects (Performance-Specific)
|
|
154
|
+
|
|
155
|
+
### Angular / Modern Frontend (Angular 16–19+)
|
|
156
|
+
| ID | Check | Category | Impact |
|
|
157
|
+
|---|---|---|---|
|
|
158
|
+
| **ANG001** | Missing OnPush Change Detection | DOM Re-render | 65% re-render reduction |
|
|
159
|
+
| **ANG002** | Direct @Input Property Mutation | Change Detection | Restores OnPush change detection |
|
|
160
|
+
| **ANG003** | `detectChanges()` Synchronously Inside Loop | Render Cycle | Eliminates N render cycles |
|
|
161
|
+
| **ANG004** | Observable Memory Leak (Missing Unsubscribe / Teardown) | Memory Leak | Eliminates heap growth per route |
|
|
162
|
+
| **ANG005** | Nested `subscribe()` Callback Anti-pattern | Concurrency | Eliminates race conditions & request leaks |
|
|
163
|
+
| **ANG006** | API Call Without Pagination (Full Collection Fetch) | Network & Heap | 70%+ TTI improvement |
|
|
164
|
+
| **ANG007** | Missing `debounceTime` on Reactive Form Streams | Network | Up to 85% fewer API requests |
|
|
165
|
+
| **ANG008** | Missing HTTP Response Caching (`shareReplay`) | Network | Eliminates duplicate network calls |
|
|
166
|
+
| **ANG009** | `*ngFor` Without `trackBy` or `@for` Without `track` | DOM Performance | 99% fewer DOM node recreations |
|
|
167
|
+
| **ANG010** | Missing Virtual Scrolling for Large Lists | DOM Performance | Renders only ~20 DOM nodes |
|
|
168
|
+
| **ANG011** | Method Calls in Template Interpolation | DOM Re-render | Eliminates expensive method re-evaluations |
|
|
169
|
+
| **ANG012** | Wildcard / Full Library Imports (`lodash`, `rxjs/Rx`) | Bundle Size | 50–80KB bundle reduction |
|
|
170
|
+
| **ANG013** | No Lazy Loading Routes (All Eager at Startup) | Bundle Size | 40–60% initial bundle cut |
|
|
171
|
+
| **ANG014** | Heavy Subcomponents Rendered Without `@defer` | Core Web Vitals | Speeds up LCP & initial chunk parse |
|
|
172
|
+
|
|
173
|
+
### React & Next.js (React 17–19+)
|
|
174
|
+
| ID | Check | Category | Impact |
|
|
175
|
+
|---|---|---|---|
|
|
176
|
+
| **REACT001** | Array Index as `key` in List Rendering | Reconciliation | Enables DOM node reuse on mutations |
|
|
177
|
+
| **REACT002** | Direct State Mutation Anti-pattern | State Management | Prevents broken re-render schedules |
|
|
178
|
+
| **REACT003** | `useEffect` Missing Dependency Array | Render Loop | Eliminates runaway infinite re-render loops |
|
|
179
|
+
| **REACT004** | Expensive Array Calculations Without `useMemo` | CPU Efficiency | Avoids heavy sorting/filtering recalculations |
|
|
180
|
+
| **REACT005** | Eager Route Components Without `React.lazy()` | Bundle Size | 30–50% smaller initial JS chunks |
|
|
181
|
+
| **REACT006** | Context Value Recreated as New Object on Render | Re-render Cascade | Stops cascading subtree re-renders |
|
|
182
|
+
|
|
183
|
+
### Node.js / Seneca / Express Backend
|
|
184
|
+
| ID | Check | Category | Impact |
|
|
185
|
+
|---|---|---|---|
|
|
186
|
+
| **NODE001** | Synchronous `fs` Calls in Runtime Execution Paths | Event Loop | Unblocks Node.js event loop |
|
|
187
|
+
| **NODE002** | Sequential `await` on Independent Operations | Concurrency | Up to 50% latency reduction |
|
|
188
|
+
| **NODE003** | Promise Chains Without `.catch()` | Stability | Prevents unhandled rejections |
|
|
189
|
+
| **NODE004** | **N+1 Database Query Pattern in Loop** | Database I/O | O(N) → O(1) query roundtrips |
|
|
190
|
+
| **NODE005** | **Database Queries Without LIMIT (Returns All Rows)** | Database I/O | Caps payload and memory bounds |
|
|
191
|
+
| **NODE006** | No Caching Layer for Repeated Queries (Redis) | Database I/O | 60–90% database load reduction |
|
|
192
|
+
| **NODE007** | API Response Over-fetching (`SELECT *` to `res.json`) | Serialization | Smaller payloads, faster JSON encode |
|
|
193
|
+
| **NODE008** | Express Missing Gzip/Brotli Compression | Network | 70–90% smaller response sizes |
|
|
194
|
+
| **NODE009** | Unbounded In-Memory Array Growth | Memory Leak | Prevents OOM crashes |
|
|
195
|
+
| **NODE010** | Database Connection Pool Configured Too Small | Database I/O | Eliminates connection queue delays |
|
|
196
|
+
| **NODE011** | **EventEmitter Memory Leak (Missing Listener Teardown)** | Memory Leak | Eliminates closure/listener leaks |
|
|
197
|
+
| **NODE012** | **HTTP Requests Missing Connection Reuse (`keepAlive: true`)** | Network | Saves 50–100ms TLS handshakes |
|
|
198
|
+
| **SEN001** | Seneca `.act()` Inside Iteration Loop | Microservice RPC | N×latency → max(latency) |
|
|
199
|
+
| **SEN002** | Seneca `.act()` Without Explicit Timeout | Stability | Prevents cascading request hangs |
|
|
200
|
+
|
|
201
|
+
### SQL / RDS / Database
|
|
202
|
+
| ID | Check | Category | Impact |
|
|
203
|
+
|---|---|---|---|
|
|
204
|
+
| **SQL001** | `SELECT *` Column Over-fetching | Database I/O | Faster queries, less I/O |
|
|
205
|
+
| **SQL002** | UPDATE/DELETE Without WHERE Clause | Data Integrity | Prevents full table locks & corruption |
|
|
206
|
+
| **SQL003** | SELECT Without LIMIT | Result Bounds | Fixed result set size |
|
|
207
|
+
| **SQL004** | **Missing Index on Foreign Key Columns** | Indexing | Converts O(N) table scan to O(log N) |
|
|
208
|
+
| **SQL005** | SQL Query Built via String Concatenation | Plan Cache | Reuses compiled query plans |
|
|
209
|
+
| **SQL006** | `LIKE '%query'` Leading Wildcard (B-Tree Incompatible) | Indexing | Enables index seeks |
|
|
210
|
+
| **SQL007** | **Deep OFFSET Pagination Antipattern** | Query Optimization | Keyset pagination saves O(N) discard |
|
|
211
|
+
|
|
212
|
+
### AWS / Serverless / Cloud Infrastructure
|
|
213
|
+
| ID | Check | Category | Impact |
|
|
214
|
+
|---|---|---|---|
|
|
215
|
+
| **AWS001** | Lambda Memory Allocated Too Low (<512MB) | CPU & Latency | 50–75% faster execution |
|
|
216
|
+
| **AWS002** | Static S3 Hosting Without CloudFront CDN | Edge Delivery | 300ms → 20ms global asset delivery |
|
|
217
|
+
| **AWS003** | Lambda Functions Without Explicit Timeout | Cost & Timeouts | Prevents runaway costs on hangs |
|
|
218
|
+
| **AWS005** | **Lambda Direct to RDS Without RDS Proxy** | Connection Pool | Prevents DB connection exhaustion |
|
|
219
|
+
|
|
220
|
+
---
|
|
221
|
+
|
|
222
|
+
## 📈 Scoring Engine
|
|
223
|
+
|
|
224
|
+
- **Asymptotic Logarithmic Curve**: Avoids artificial zero-score cliffs; accurately reflects true performance health for codebases of any size.
|
|
225
|
+
- **Architectural Layer Weights**:
|
|
226
|
+
$$\text{Overall} = 40\% \text{Frontend} + 30\% \text{Backend} + 20\% \text{Database} + 10\% \text{Infra}$$
|
|
227
|
+
- **Priority Rank**: Calculated via $\text{Impact} / \max(\text{Effort}, 1)$ to prioritize high-impact, low-effort wins.
|
|
228
|
+
|
|
229
|
+
---
|
|
230
|
+
|
|
231
|
+
## 🧪 Automated Testing
|
|
232
|
+
|
|
233
|
+
Run the included unit test suite:
|
|
234
|
+
```bash
|
|
235
|
+
python -m unittest discover -s tests -p "test_*.py" -v
|
|
236
|
+
```
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
optimizer.py,sha256=jYzzYY2gHi9KJZz8imEemfXB9-c9DGrgggYrVk0Oq8s,15309
|
|
2
|
+
analyzers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3
|
+
analyzers/angular_analyzer.py,sha256=UvkfTo3TZLPr7VseslcwP3qDxqt_v-n6a37xe8HxHko,23901
|
|
4
|
+
analyzers/aws_analyzer.py,sha256=sEyCWv3p36deftVe2wgxNILXAt3mtV4zkUoiFTpECQ8,6670
|
|
5
|
+
analyzers/node_analyzer.py,sha256=AEQKtI0g_YxWsV9r4HgJdbBYKOv0FNl9g8xqli2nWfY,18093
|
|
6
|
+
analyzers/react_analyzer.py,sha256=PlAhlDqUllVLOgnN1CVVHtRWgbOofUrq22ACh1jd0Ao,13332
|
|
7
|
+
analyzers/sql_analyzer.py,sha256=FExpp-9OA2FaajmwzHIh-JlP85tM9NsxZiLtEcBRR9k,10205
|
|
8
|
+
core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
9
|
+
core/issue.py,sha256=snKmRSeNZb_wLX8uAdJ3ntMAjAKcABuVisbuxegvC1Q,1893
|
|
10
|
+
core/scorer.py,sha256=zLGzxBkCxE3mz9J1_h6mk41Yc1UxTxyizSow5IVjoQw,3821
|
|
11
|
+
performance_optimizer-2.5.0.dist-info/licenses/LICENSE,sha256=-mOdQJ_EYbSVRnT-XOh0MmAbdBFoH2w8_llqcxHs57k,1079
|
|
12
|
+
reporter/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
13
|
+
reporter/html_reporter.py,sha256=i0MT232a4RC0I027XPNe6wI8Q7MD9s7dkZBkDclWeUs,51997
|
|
14
|
+
performance_optimizer-2.5.0.dist-info/METADATA,sha256=tgUb9YgwuMxrPH-sonB8TXWm1RzNqAF-54QkO2KqWNM,11735
|
|
15
|
+
performance_optimizer-2.5.0.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
|
|
16
|
+
performance_optimizer-2.5.0.dist-info/entry_points.txt,sha256=_pux5PibB9PvVJgNwAYOf0JlEV_SIRJLxdk57cg-gC8,89
|
|
17
|
+
performance_optimizer-2.5.0.dist-info/top_level.txt,sha256=GlwwpDftYMHkQlHF0kBSvUXtJFaARCBoPn_kCwyMEss,34
|
|
18
|
+
performance_optimizer-2.5.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Ravi Kumar Vishwakarma
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
reporter/__init__.py
ADDED
|
File without changes
|