@oalacea/daemon 0.6.4 → 0.7.1
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/README.md +268 -58
- package/bin/Dockerfile +158 -16
- package/bin/docker-entrypoint.sh +293 -0
- package/dist/cli/cli.d.ts.map +1 -1
- package/dist/cli/cli.js +22 -2
- package/dist/cli/cli.js.map +1 -1
- package/dist/cli/commands/command.types.d.ts +216 -0
- package/dist/cli/commands/command.types.d.ts.map +1 -0
- package/dist/cli/commands/command.types.js +64 -0
- package/dist/cli/commands/command.types.js.map +1 -0
- package/dist/cli/commands/history.command.d.ts +91 -0
- package/dist/cli/commands/history.command.d.ts.map +1 -0
- package/dist/cli/commands/history.command.js +336 -0
- package/dist/cli/commands/history.command.js.map +1 -0
- package/dist/cli/commands/index.d.ts +14 -3
- package/dist/cli/commands/index.d.ts.map +1 -1
- package/dist/cli/commands/index.js +7 -0
- package/dist/cli/commands/index.js.map +1 -1
- package/dist/cli/commands/optimize.command.d.ts +110 -0
- package/dist/cli/commands/optimize.command.d.ts.map +1 -0
- package/dist/cli/commands/optimize.command.js +497 -0
- package/dist/cli/commands/optimize.command.js.map +1 -0
- package/dist/cli/commands/report.command.d.ts +110 -0
- package/dist/cli/commands/report.command.d.ts.map +1 -0
- package/dist/cli/commands/report.command.js +532 -0
- package/dist/cli/commands/report.command.js.map +1 -0
- package/dist/cli/commands/review.command.d.ts +110 -0
- package/dist/cli/commands/review.command.d.ts.map +1 -0
- package/dist/cli/commands/review.command.js +520 -0
- package/dist/cli/commands/review.command.js.map +1 -0
- package/dist/cli/commands/score.command.d.ts +47 -0
- package/dist/cli/commands/score.command.d.ts.map +1 -0
- package/dist/cli/commands/score.command.js +261 -0
- package/dist/cli/commands/score.command.js.map +1 -0
- package/dist/cli/utils/index.d.ts +10 -0
- package/dist/cli/utils/index.d.ts.map +1 -0
- package/dist/cli/utils/index.js +10 -0
- package/dist/cli/utils/index.js.map +1 -0
- package/dist/cli/utils/output.d.ts +192 -0
- package/dist/cli/utils/output.d.ts.map +1 -0
- package/dist/cli/utils/output.js +411 -0
- package/dist/cli/utils/output.js.map +1 -0
- package/dist/cli/utils/progress.d.ts +204 -0
- package/dist/cli/utils/progress.d.ts.map +1 -0
- package/dist/cli/utils/progress.js +396 -0
- package/dist/cli/utils/progress.js.map +1 -0
- package/dist/core/types/index.d.ts +1 -0
- package/dist/core/types/index.d.ts.map +1 -1
- package/dist/core/types/project.types.d.ts +3 -3
- package/dist/core/types/project.types.d.ts.map +1 -1
- package/dist/core/types/scoring.types.d.ts +301 -0
- package/dist/core/types/scoring.types.d.ts.map +1 -0
- package/dist/core/types/scoring.types.js +8 -0
- package/dist/core/types/scoring.types.js.map +1 -0
- package/dist/services/detection/framework-detector.d.ts.map +1 -1
- package/dist/services/detection/framework-detector.js +74 -5
- package/dist/services/detection/framework-detector.js.map +1 -1
- package/dist/services/index.d.ts +12 -0
- package/dist/services/index.d.ts.map +1 -1
- package/dist/services/index.js +14 -0
- package/dist/services/index.js.map +1 -1
- package/dist/services/optimization/detectors/bug-detector.d.ts +82 -0
- package/dist/services/optimization/detectors/bug-detector.d.ts.map +1 -0
- package/dist/services/optimization/detectors/bug-detector.js +443 -0
- package/dist/services/optimization/detectors/bug-detector.js.map +1 -0
- package/dist/services/optimization/detectors/code-smell-detector.d.ts +108 -0
- package/dist/services/optimization/detectors/code-smell-detector.d.ts.map +1 -0
- package/dist/services/optimization/detectors/code-smell-detector.js +569 -0
- package/dist/services/optimization/detectors/code-smell-detector.js.map +1 -0
- package/dist/services/optimization/detectors/index.d.ts +7 -0
- package/dist/services/optimization/detectors/index.d.ts.map +1 -0
- package/dist/services/optimization/detectors/index.js +7 -0
- package/dist/services/optimization/detectors/index.js.map +1 -0
- package/dist/services/optimization/detectors/perf-detector.d.ts +80 -0
- package/dist/services/optimization/detectors/perf-detector.d.ts.map +1 -0
- package/dist/services/optimization/detectors/perf-detector.js +451 -0
- package/dist/services/optimization/detectors/perf-detector.js.map +1 -0
- package/dist/services/optimization/index.d.ts +61 -0
- package/dist/services/optimization/index.d.ts.map +1 -0
- package/dist/services/optimization/index.js +69 -0
- package/dist/services/optimization/index.js.map +1 -0
- package/dist/services/optimization/optimization.service.d.ts +65 -0
- package/dist/services/optimization/optimization.service.d.ts.map +1 -0
- package/dist/services/optimization/optimization.service.js +511 -0
- package/dist/services/optimization/optimization.service.js.map +1 -0
- package/dist/services/optimization/optimization.types.d.ts +343 -0
- package/dist/services/optimization/optimization.types.d.ts.map +1 -0
- package/dist/services/optimization/optimization.types.js +8 -0
- package/dist/services/optimization/optimization.types.js.map +1 -0
- package/dist/services/optimization/optimizers/code-optimizer.d.ts +87 -0
- package/dist/services/optimization/optimizers/code-optimizer.d.ts.map +1 -0
- package/dist/services/optimization/optimizers/code-optimizer.js +436 -0
- package/dist/services/optimization/optimizers/code-optimizer.js.map +1 -0
- package/dist/services/optimization/optimizers/index.d.ts +7 -0
- package/dist/services/optimization/optimizers/index.d.ts.map +1 -0
- package/dist/services/optimization/optimizers/index.js +7 -0
- package/dist/services/optimization/optimizers/index.js.map +1 -0
- package/dist/services/optimization/optimizers/perf-optimizer.d.ts +64 -0
- package/dist/services/optimization/optimizers/perf-optimizer.d.ts.map +1 -0
- package/dist/services/optimization/optimizers/perf-optimizer.js +330 -0
- package/dist/services/optimization/optimizers/perf-optimizer.js.map +1 -0
- package/dist/services/optimization/optimizers/refact-optimizer.d.ts +82 -0
- package/dist/services/optimization/optimizers/refact-optimizer.d.ts.map +1 -0
- package/dist/services/optimization/optimizers/refact-optimizer.js +354 -0
- package/dist/services/optimization/optimizers/refact-optimizer.js.map +1 -0
- package/dist/services/optimization/patterns/anti-patterns.d.ts +31 -0
- package/dist/services/optimization/patterns/anti-patterns.d.ts.map +1 -0
- package/dist/services/optimization/patterns/anti-patterns.js +501 -0
- package/dist/services/optimization/patterns/anti-patterns.js.map +1 -0
- package/dist/services/optimization/patterns/index.d.ts +5 -0
- package/dist/services/optimization/patterns/index.d.ts.map +1 -0
- package/dist/services/optimization/patterns/index.js +5 -0
- package/dist/services/optimization/patterns/index.js.map +1 -0
- package/dist/services/reporting/export/chart.exporter.d.ts +59 -0
- package/dist/services/reporting/export/chart.exporter.d.ts.map +1 -0
- package/dist/services/reporting/export/chart.exporter.js +350 -0
- package/dist/services/reporting/export/chart.exporter.js.map +1 -0
- package/dist/services/reporting/export/index.d.ts +9 -0
- package/dist/services/reporting/export/index.d.ts.map +1 -0
- package/dist/services/reporting/export/index.js +10 -0
- package/dist/services/reporting/export/index.js.map +1 -0
- package/dist/services/reporting/export/pdf.exporter.d.ts +133 -0
- package/dist/services/reporting/export/pdf.exporter.d.ts.map +1 -0
- package/dist/services/reporting/export/pdf.exporter.js +270 -0
- package/dist/services/reporting/export/pdf.exporter.js.map +1 -0
- package/dist/services/reporting/history.service.d.ts +93 -0
- package/dist/services/reporting/history.service.d.ts.map +1 -0
- package/dist/services/reporting/history.service.js +285 -0
- package/dist/services/reporting/history.service.js.map +1 -0
- package/dist/services/reporting/index.d.ts +15 -0
- package/dist/services/reporting/index.d.ts.map +1 -0
- package/dist/services/reporting/index.js +16 -0
- package/dist/services/reporting/index.js.map +1 -0
- package/dist/services/reporting/report.service.d.ts +102 -0
- package/dist/services/reporting/report.service.d.ts.map +1 -0
- package/dist/services/reporting/report.service.js +240 -0
- package/dist/services/reporting/report.service.js.map +1 -0
- package/dist/services/reporting/reporting.types.d.ts +329 -0
- package/dist/services/reporting/reporting.types.d.ts.map +1 -0
- package/dist/services/reporting/reporting.types.js +8 -0
- package/dist/services/reporting/reporting.types.js.map +1 -0
- package/dist/services/reporting/templates/html.template.d.ts +81 -0
- package/dist/services/reporting/templates/html.template.d.ts.map +1 -0
- package/dist/services/reporting/templates/html.template.js +741 -0
- package/dist/services/reporting/templates/html.template.js.map +1 -0
- package/dist/services/reporting/templates/json.template.d.ts +85 -0
- package/dist/services/reporting/templates/json.template.d.ts.map +1 -0
- package/dist/services/reporting/templates/json.template.js +308 -0
- package/dist/services/reporting/templates/json.template.js.map +1 -0
- package/dist/services/reporting/templates/markdown.template.d.ts +69 -0
- package/dist/services/reporting/templates/markdown.template.d.ts.map +1 -0
- package/dist/services/reporting/templates/markdown.template.js +311 -0
- package/dist/services/reporting/templates/markdown.template.js.map +1 -0
- package/dist/services/reporting/trend-analyzer.d.ts +73 -0
- package/dist/services/reporting/trend-analyzer.d.ts.map +1 -0
- package/dist/services/reporting/trend-analyzer.js +291 -0
- package/dist/services/reporting/trend-analyzer.js.map +1 -0
- package/dist/services/review/analyzers/dependency-analyzer.d.ts +87 -0
- package/dist/services/review/analyzers/dependency-analyzer.d.ts.map +1 -0
- package/dist/services/review/analyzers/dependency-analyzer.js +458 -0
- package/dist/services/review/analyzers/dependency-analyzer.js.map +1 -0
- package/dist/services/review/analyzers/index.d.ts +13 -0
- package/dist/services/review/analyzers/index.d.ts.map +1 -0
- package/dist/services/review/analyzers/index.js +13 -0
- package/dist/services/review/analyzers/index.js.map +1 -0
- package/dist/services/review/analyzers/nestjs-analyzer.d.ts +210 -0
- package/dist/services/review/analyzers/nestjs-analyzer.d.ts.map +1 -0
- package/dist/services/review/analyzers/nestjs-analyzer.js +571 -0
- package/dist/services/review/analyzers/nestjs-analyzer.js.map +1 -0
- package/dist/services/review/analyzers/performance-analyzer.d.ts +91 -0
- package/dist/services/review/analyzers/performance-analyzer.d.ts.map +1 -0
- package/dist/services/review/analyzers/performance-analyzer.js +589 -0
- package/dist/services/review/analyzers/performance-analyzer.js.map +1 -0
- package/dist/services/review/analyzers/security-analyzer.d.ts +96 -0
- package/dist/services/review/analyzers/security-analyzer.d.ts.map +1 -0
- package/dist/services/review/analyzers/security-analyzer.js +512 -0
- package/dist/services/review/analyzers/security-analyzer.js.map +1 -0
- package/dist/services/review/analyzers/static-analyzer.d.ts +90 -0
- package/dist/services/review/analyzers/static-analyzer.d.ts.map +1 -0
- package/dist/services/review/analyzers/static-analyzer.js +423 -0
- package/dist/services/review/analyzers/static-analyzer.js.map +1 -0
- package/dist/services/review/fixers/auto-fixer.d.ts +94 -0
- package/dist/services/review/fixers/auto-fixer.d.ts.map +1 -0
- package/dist/services/review/fixers/auto-fixer.js +404 -0
- package/dist/services/review/fixers/auto-fixer.js.map +1 -0
- package/dist/services/review/fixers/index.d.ts +11 -0
- package/dist/services/review/fixers/index.d.ts.map +1 -0
- package/dist/services/review/fixers/index.js +11 -0
- package/dist/services/review/fixers/index.js.map +1 -0
- package/dist/services/review/fixers/refactor-suggester.d.ts +100 -0
- package/dist/services/review/fixers/refactor-suggester.d.ts.map +1 -0
- package/dist/services/review/fixers/refactor-suggester.js +555 -0
- package/dist/services/review/fixers/refactor-suggester.js.map +1 -0
- package/dist/services/review/fixers/test-generator.d.ts +99 -0
- package/dist/services/review/fixers/test-generator.d.ts.map +1 -0
- package/dist/services/review/fixers/test-generator.js +458 -0
- package/dist/services/review/fixers/test-generator.js.map +1 -0
- package/dist/services/review/index.d.ts +14 -0
- package/dist/services/review/index.d.ts.map +1 -0
- package/dist/services/review/index.js +14 -0
- package/dist/services/review/index.js.map +1 -0
- package/dist/services/review/reporters/fix-reporter.d.ts +67 -0
- package/dist/services/review/reporters/fix-reporter.d.ts.map +1 -0
- package/dist/services/review/reporters/fix-reporter.js +437 -0
- package/dist/services/review/reporters/fix-reporter.js.map +1 -0
- package/dist/services/review/reporters/index.d.ts +10 -0
- package/dist/services/review/reporters/index.d.ts.map +1 -0
- package/dist/services/review/reporters/index.js +10 -0
- package/dist/services/review/reporters/index.js.map +1 -0
- package/dist/services/review/reporters/score-reporter.d.ts +84 -0
- package/dist/services/review/reporters/score-reporter.d.ts.map +1 -0
- package/dist/services/review/reporters/score-reporter.js +560 -0
- package/dist/services/review/reporters/score-reporter.js.map +1 -0
- package/dist/services/review/review.service.d.ts +129 -0
- package/dist/services/review/review.service.d.ts.map +1 -0
- package/dist/services/review/review.service.js +396 -0
- package/dist/services/review/review.service.js.map +1 -0
- package/dist/services/review/review.types.d.ts +443 -0
- package/dist/services/review/review.types.d.ts.map +1 -0
- package/dist/services/review/review.types.js +11 -0
- package/dist/services/review/review.types.js.map +1 -0
- package/dist/services/scoring/dimensions/accessibility.analyzer.d.ts +53 -0
- package/dist/services/scoring/dimensions/accessibility.analyzer.d.ts.map +1 -0
- package/dist/services/scoring/dimensions/accessibility.analyzer.js +260 -0
- package/dist/services/scoring/dimensions/accessibility.analyzer.js.map +1 -0
- package/dist/services/scoring/dimensions/backend-logic.analyzer.d.ts +138 -0
- package/dist/services/scoring/dimensions/backend-logic.analyzer.d.ts.map +1 -0
- package/dist/services/scoring/dimensions/backend-logic.analyzer.js +713 -0
- package/dist/services/scoring/dimensions/backend-logic.analyzer.js.map +1 -0
- package/dist/services/scoring/dimensions/business-logic.analyzer.d.ts +142 -0
- package/dist/services/scoring/dimensions/business-logic.analyzer.d.ts.map +1 -0
- package/dist/services/scoring/dimensions/business-logic.analyzer.js +747 -0
- package/dist/services/scoring/dimensions/business-logic.analyzer.js.map +1 -0
- package/dist/services/scoring/dimensions/code-quality.analyzer.d.ts +142 -0
- package/dist/services/scoring/dimensions/code-quality.analyzer.d.ts.map +1 -0
- package/dist/services/scoring/dimensions/code-quality.analyzer.js +685 -0
- package/dist/services/scoring/dimensions/code-quality.analyzer.js.map +1 -0
- package/dist/services/scoring/dimensions/index.d.ts +18 -0
- package/dist/services/scoring/dimensions/index.d.ts.map +1 -0
- package/dist/services/scoring/dimensions/index.js +27 -0
- package/dist/services/scoring/dimensions/index.js.map +1 -0
- package/dist/services/scoring/dimensions/performance.analyzer.d.ts +125 -0
- package/dist/services/scoring/dimensions/performance.analyzer.d.ts.map +1 -0
- package/dist/services/scoring/dimensions/performance.analyzer.js +615 -0
- package/dist/services/scoring/dimensions/performance.analyzer.js.map +1 -0
- package/dist/services/scoring/dimensions/security.analyzer.d.ts +53 -0
- package/dist/services/scoring/dimensions/security.analyzer.d.ts.map +1 -0
- package/dist/services/scoring/dimensions/security.analyzer.js +327 -0
- package/dist/services/scoring/dimensions/security.analyzer.js.map +1 -0
- package/dist/services/scoring/dimensions/seo.analyzer.d.ts +77 -0
- package/dist/services/scoring/dimensions/seo.analyzer.d.ts.map +1 -0
- package/dist/services/scoring/dimensions/seo.analyzer.js +502 -0
- package/dist/services/scoring/dimensions/seo.analyzer.js.map +1 -0
- package/dist/services/scoring/dimensions/test-coverage.analyzer.d.ts +106 -0
- package/dist/services/scoring/dimensions/test-coverage.analyzer.d.ts.map +1 -0
- package/dist/services/scoring/dimensions/test-coverage.analyzer.js +496 -0
- package/dist/services/scoring/dimensions/test-coverage.analyzer.js.map +1 -0
- package/dist/services/scoring/dimensions/ui-ux.analyzer.d.ts +126 -0
- package/dist/services/scoring/dimensions/ui-ux.analyzer.d.ts.map +1 -0
- package/dist/services/scoring/dimensions/ui-ux.analyzer.js +665 -0
- package/dist/services/scoring/dimensions/ui-ux.analyzer.js.map +1 -0
- package/dist/services/scoring/index.d.ts +10 -0
- package/dist/services/scoring/index.d.ts.map +1 -0
- package/dist/services/scoring/index.js +10 -0
- package/dist/services/scoring/index.js.map +1 -0
- package/dist/services/scoring/scoring-service.d.ts +222 -0
- package/dist/services/scoring/scoring-service.d.ts.map +1 -0
- package/dist/services/scoring/scoring-service.js +636 -0
- package/dist/services/scoring/scoring-service.js.map +1 -0
- package/package.json +13 -4
- package/templates/README.md +183 -0
- package/templates/nestjs/controller.spec.ts +203 -0
- package/templates/nestjs/e2e/api.e2e-spec.ts +451 -0
- package/templates/nestjs/e2e/auth.e2e-spec.ts +533 -0
- package/templates/nestjs/fixtures/test-module.ts +311 -0
- package/templates/nestjs/guard.spec.ts +314 -0
- package/templates/nestjs/interceptor.spec.ts +458 -0
- package/templates/nestjs/module.spec.ts +173 -0
- package/templates/nestjs/pipe.spec.ts +474 -0
- package/templates/nestjs/service.spec.ts +296 -0
- package/templates/rust/Cargo.toml +72 -0
- package/templates/rust/actix-controller.test.rs +114 -0
- package/templates/rust/axum-handler.test.rs +117 -0
- package/templates/rust/integration.test.rs +63 -0
- package/templates/rust/rocket-route.test.rs +106 -0
- package/templates/rust/unit.test.rs +38 -0
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Performance Analyzer
|
|
3
|
+
*
|
|
4
|
+
* Analyse les problèmes de performance:
|
|
5
|
+
* - Taille du bundle
|
|
6
|
+
* - Opportunités de lazy loading
|
|
7
|
+
* - Re-renders inutiles (React)
|
|
8
|
+
* - Requêtes N+1
|
|
9
|
+
* - Index manquants
|
|
10
|
+
*
|
|
11
|
+
* @module services/review/analyzers/performance-analyzer
|
|
12
|
+
*/
|
|
13
|
+
import type { Issue, PerformanceMetrics, PerformanceAnalyzerConfig } from '../review.types.js';
|
|
14
|
+
import { DockerManager } from '../../docker/docker-manager.js';
|
|
15
|
+
/**
|
|
16
|
+
* Analyseur de performance
|
|
17
|
+
*/
|
|
18
|
+
export declare class PerformanceAnalyzer {
|
|
19
|
+
private readonly docker;
|
|
20
|
+
private readonly logger;
|
|
21
|
+
private config;
|
|
22
|
+
private issueCounter;
|
|
23
|
+
constructor(config?: PerformanceAnalyzerConfig, docker?: DockerManager);
|
|
24
|
+
/**
|
|
25
|
+
* Configure l'analyseur
|
|
26
|
+
*/
|
|
27
|
+
configure(config: Partial<PerformanceAnalyzerConfig>): void;
|
|
28
|
+
/**
|
|
29
|
+
* Analyse les performances d'un projet
|
|
30
|
+
*
|
|
31
|
+
* @param projectPath - Chemin du projet à analyser
|
|
32
|
+
* @returns Métriques de performance et issues
|
|
33
|
+
*/
|
|
34
|
+
analyze(projectPath: string): Promise<{
|
|
35
|
+
metrics: PerformanceMetrics;
|
|
36
|
+
issues: Issue[];
|
|
37
|
+
}>;
|
|
38
|
+
/**
|
|
39
|
+
* Analyse la taille du bundle
|
|
40
|
+
*/
|
|
41
|
+
private analyzeBundleSize;
|
|
42
|
+
/**
|
|
43
|
+
* Calcule la taille d'un dossier
|
|
44
|
+
*/
|
|
45
|
+
private calculateDirSize;
|
|
46
|
+
/**
|
|
47
|
+
* Estime la taille du bundle à partir des dépendances
|
|
48
|
+
*/
|
|
49
|
+
private estimateBundleSizeFromDeps;
|
|
50
|
+
/**
|
|
51
|
+
* Détecte les opportunités de lazy loading
|
|
52
|
+
*/
|
|
53
|
+
private detectLazyLoading;
|
|
54
|
+
/**
|
|
55
|
+
* Détecte les re-renders inutiles React
|
|
56
|
+
*/
|
|
57
|
+
private detectRerenders;
|
|
58
|
+
/**
|
|
59
|
+
* Détecte les requêtes N+1
|
|
60
|
+
*/
|
|
61
|
+
private detectNPlusOneQueries;
|
|
62
|
+
/**
|
|
63
|
+
* Détecte les index manquants (pour Prisma/TypeORM)
|
|
64
|
+
*/
|
|
65
|
+
private detectMissingIndexes;
|
|
66
|
+
/**
|
|
67
|
+
* Trouve les composants déjà lazy-loadés
|
|
68
|
+
*/
|
|
69
|
+
private findLazyComponents;
|
|
70
|
+
/**
|
|
71
|
+
* Trouve tous les fichiers source
|
|
72
|
+
*/
|
|
73
|
+
private findSourceFiles;
|
|
74
|
+
/**
|
|
75
|
+
* Trouve la position ligne/colonne à partir d'un index
|
|
76
|
+
*/
|
|
77
|
+
private findPosition;
|
|
78
|
+
/**
|
|
79
|
+
* Extrait un extrait de code
|
|
80
|
+
*/
|
|
81
|
+
private extractCodeSnippet;
|
|
82
|
+
/**
|
|
83
|
+
* Formate une taille en octets pour l'affichage
|
|
84
|
+
*/
|
|
85
|
+
private formatBytes;
|
|
86
|
+
/**
|
|
87
|
+
* Génère un ID unique
|
|
88
|
+
*/
|
|
89
|
+
private generateIssueId;
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=performance-analyzer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"performance-analyzer.d.ts","sourceRoot":"","sources":["../../../../src/services/review/analyzers/performance-analyzer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAMH,OAAO,KAAK,EACV,KAAK,EACL,kBAAkB,EAElB,yBAAyB,EAC1B,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AAkF/D;;GAEG;AACH,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAgB;IACvC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,MAAM,CAA4B;IAC1C,OAAO,CAAC,YAAY,CAAK;gBAEb,MAAM,GAAE,yBAA8B,EAAE,MAAM,CAAC,EAAE,aAAa;IAM1E;;OAEG;IACH,SAAS,CAAC,MAAM,EAAE,OAAO,CAAC,yBAAyB,CAAC,GAAG,IAAI;IAI3D;;;;;OAKG;IACG,OAAO,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,kBAAkB,CAAC;QAAC,MAAM,EAAE,KAAK,EAAE,CAAA;KAAE,CAAC;IA0F7F;;OAEG;YACW,iBAAiB;IA6B/B;;OAEG;YACW,gBAAgB;IA2C9B;;OAEG;YACW,0BAA0B;IAgDxC;;OAEG;YACW,iBAAiB;IAiD/B;;OAEG;YACW,eAAe;IA8C7B;;OAEG;YACW,qBAAqB;IA0DnC;;OAEG;YACW,oBAAoB;IAqDlC;;OAEG;YACW,kBAAkB;IAmBhC;;OAEG;YACW,eAAe;IAgC7B;;OAEG;IACH,OAAO,CAAC,YAAY;IASpB;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAM1B;;OAEG;IACH,OAAO,CAAC,WAAW;IAQnB;;OAEG;IACH,OAAO,CAAC,eAAe;CAGxB"}
|
|
@@ -0,0 +1,589 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Performance Analyzer
|
|
3
|
+
*
|
|
4
|
+
* Analyse les problèmes de performance:
|
|
5
|
+
* - Taille du bundle
|
|
6
|
+
* - Opportunités de lazy loading
|
|
7
|
+
* - Re-renders inutiles (React)
|
|
8
|
+
* - Requêtes N+1
|
|
9
|
+
* - Index manquants
|
|
10
|
+
*
|
|
11
|
+
* @module services/review/analyzers/performance-analyzer
|
|
12
|
+
*/
|
|
13
|
+
import { readFile, readdir } from 'node:fs/promises';
|
|
14
|
+
import { join, relative } from 'node:path';
|
|
15
|
+
import { access, constants } from 'node:fs/promises';
|
|
16
|
+
import { DockerManager } from '../../docker/docker-manager.js';
|
|
17
|
+
import { createLogger } from '../../../shared/utils/logger.js';
|
|
18
|
+
/**
|
|
19
|
+
* Extensions de fichiers source
|
|
20
|
+
*/
|
|
21
|
+
const SOURCE_EXTENSIONS = ['.ts', '.tsx', '.js', '.jsx', '.vue', '.svelte'];
|
|
22
|
+
/**
|
|
23
|
+
* Dossiers à exclure
|
|
24
|
+
*/
|
|
25
|
+
const EXCLUDE_DIRS = ['node_modules', '.git', 'dist', 'build', 'coverage', '.next', '.nuxt', 'out'];
|
|
26
|
+
/**
|
|
27
|
+
* Patterns de code indiquant un manque de lazy loading
|
|
28
|
+
*/
|
|
29
|
+
const LAZY_LOAD_PATTERNS = [
|
|
30
|
+
{
|
|
31
|
+
pattern: /import\s+.*?\s+from\s+['"`](?![@\.]).*?['"`]/g,
|
|
32
|
+
description: 'Eager import of library component',
|
|
33
|
+
suggestion: 'Use dynamic import() for code splitting',
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
pattern: /lazy\s*=\s*false|Suspense.*?fallback\s*=\s*null/g,
|
|
37
|
+
description: 'Explicit lazy loading disabled',
|
|
38
|
+
suggestion: 'Enable lazy loading for better initial load time',
|
|
39
|
+
},
|
|
40
|
+
];
|
|
41
|
+
/**
|
|
42
|
+
* Patterns React pour détecter les re-renders inutiles
|
|
43
|
+
*/
|
|
44
|
+
const RE_RENDER_PATTERNS = [
|
|
45
|
+
{
|
|
46
|
+
pattern: /useState\s*\(\[\]\)/g,
|
|
47
|
+
description: 'Array state without memo',
|
|
48
|
+
suggestion: 'Use useMemo if array is used in dependencies',
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
pattern: /new\s+(?:Array|Object|Date)\s*\([^)]*\)\s*[,\)]/g,
|
|
52
|
+
description: 'Creating new object/array in render',
|
|
53
|
+
suggestion: 'Move to useMemo or outside component',
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
pattern: /function\s+\w+\s*\([^)]*\)\s*\{[^}]*\{[^}]*\.map\s*\(/g,
|
|
57
|
+
description: 'Inline function with map in render',
|
|
58
|
+
suggestion: 'Extract component or use useCallback',
|
|
59
|
+
},
|
|
60
|
+
];
|
|
61
|
+
/**
|
|
62
|
+
* Patterns de requêtes N+1
|
|
63
|
+
*/
|
|
64
|
+
const N_PLUS_ONE_PATTERNS = [
|
|
65
|
+
{
|
|
66
|
+
pattern: /forEach.*?(?:find|query|fetch|select)/gi,
|
|
67
|
+
description: 'Query inside forEach loop - potential N+1',
|
|
68
|
+
suggestion: 'Use batch loading or include relations',
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
pattern: /for\s*\([^)]*\)\s*\{[^}]*?(?:find|query|fetch|select)/gi,
|
|
72
|
+
description: 'Query inside for loop - potential N+1',
|
|
73
|
+
suggestion: 'Use batch loading or include relations',
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
pattern: /\.map\s*\([^)]*\)\s*=>\s*\{[^}]*?(?:find|query|fetch|select)/gi,
|
|
77
|
+
description: 'Query inside map - potential N+1',
|
|
78
|
+
suggestion: 'Use batch loading or include relations',
|
|
79
|
+
},
|
|
80
|
+
];
|
|
81
|
+
/**
|
|
82
|
+
* Configuration par défaut
|
|
83
|
+
*/
|
|
84
|
+
const DEFAULT_CONFIG = {
|
|
85
|
+
analyzeBundleSize: true,
|
|
86
|
+
detectLazyOpportunities: true,
|
|
87
|
+
detectRerenders: true,
|
|
88
|
+
detectNPlusOne: true,
|
|
89
|
+
detectMissingIndexes: true,
|
|
90
|
+
};
|
|
91
|
+
/**
|
|
92
|
+
* Analyseur de performance
|
|
93
|
+
*/
|
|
94
|
+
export class PerformanceAnalyzer {
|
|
95
|
+
docker;
|
|
96
|
+
logger;
|
|
97
|
+
config;
|
|
98
|
+
issueCounter = 0;
|
|
99
|
+
constructor(config = {}, docker) {
|
|
100
|
+
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
101
|
+
this.docker = docker ?? new DockerManager();
|
|
102
|
+
this.logger = createLogger('PerformanceAnalyzer');
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Configure l'analyseur
|
|
106
|
+
*/
|
|
107
|
+
configure(config) {
|
|
108
|
+
this.config = { ...this.config, ...config };
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Analyse les performances d'un projet
|
|
112
|
+
*
|
|
113
|
+
* @param projectPath - Chemin du projet à analyser
|
|
114
|
+
* @returns Métriques de performance et issues
|
|
115
|
+
*/
|
|
116
|
+
async analyze(projectPath) {
|
|
117
|
+
this.issueCounter = 0;
|
|
118
|
+
const startTime = performance.now();
|
|
119
|
+
this.logger.info(`Starting performance analysis for: ${projectPath}`);
|
|
120
|
+
const metrics = {
|
|
121
|
+
lazyLoadingOpportunities: 0,
|
|
122
|
+
unnecessaryRerenders: 0,
|
|
123
|
+
nPlusOneQueries: [],
|
|
124
|
+
missingIndexes: [],
|
|
125
|
+
lazyComponents: [],
|
|
126
|
+
};
|
|
127
|
+
const issues = [];
|
|
128
|
+
// Analyser la taille du bundle
|
|
129
|
+
if (this.config.analyzeBundleSize) {
|
|
130
|
+
const bundleSize = await this.analyzeBundleSize(projectPath);
|
|
131
|
+
metrics.bundleSize = bundleSize;
|
|
132
|
+
if (bundleSize.total > 500_000) {
|
|
133
|
+
// 500 KB warning threshold
|
|
134
|
+
issues.push({
|
|
135
|
+
id: this.generateIssueId('bundle-size'),
|
|
136
|
+
category: 'performance',
|
|
137
|
+
severity: bundleSize.total > 1_000_000 ? 'high' : 'medium',
|
|
138
|
+
description: 'Large bundle size detected',
|
|
139
|
+
message: `Total bundle size is ${this.formatBytes(bundleSize.total)}. Consider code splitting and tree shaking.`,
|
|
140
|
+
location: {
|
|
141
|
+
file: join(projectPath, 'package.json'),
|
|
142
|
+
line: 1,
|
|
143
|
+
},
|
|
144
|
+
fixable: false,
|
|
145
|
+
effort: 6,
|
|
146
|
+
suggestions: [
|
|
147
|
+
'Implement code splitting with dynamic imports',
|
|
148
|
+
'Use bundle analyzer to identify large modules',
|
|
149
|
+
'Remove unused dependencies',
|
|
150
|
+
'Enable tree shaking in bundler config',
|
|
151
|
+
],
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
// Détecter les opportunités de lazy loading
|
|
156
|
+
if (this.config.detectLazyOpportunities) {
|
|
157
|
+
const lazyIssues = await this.detectLazyLoading(projectPath);
|
|
158
|
+
metrics.lazyLoadingOpportunities = lazyIssues.length;
|
|
159
|
+
issues.push(...lazyIssues);
|
|
160
|
+
}
|
|
161
|
+
// Détecter les re-renders React
|
|
162
|
+
if (this.config.detectRerenders) {
|
|
163
|
+
const rerenderIssues = await this.detectRerenders(projectPath);
|
|
164
|
+
metrics.unnecessaryRerenders = rerenderIssues.length;
|
|
165
|
+
issues.push(...rerenderIssues);
|
|
166
|
+
}
|
|
167
|
+
// Détecter les requêtes N+1
|
|
168
|
+
if (this.config.detectNPlusOne) {
|
|
169
|
+
const nPlusOneIssues = await this.detectNPlusOneQueries(projectPath);
|
|
170
|
+
metrics.nPlusOneQueries = nPlusOneIssues.map((i) => ({
|
|
171
|
+
file: i.location.file,
|
|
172
|
+
line: i.location.line,
|
|
173
|
+
description: i.description,
|
|
174
|
+
}));
|
|
175
|
+
issues.push(...nPlusOneIssues);
|
|
176
|
+
}
|
|
177
|
+
// Détecter les index manquants
|
|
178
|
+
if (this.config.detectMissingIndexes) {
|
|
179
|
+
const missingIndexIssues = await this.detectMissingIndexes(projectPath);
|
|
180
|
+
metrics.missingIndexes = missingIndexIssues.map((i) => ({
|
|
181
|
+
table: i.description.split(' on ')[1]?.split(' ')[0] || 'unknown',
|
|
182
|
+
column: i.description.split(' on ')[1]?.split(' ')[1]?.replace(/[()]/g, '') || 'unknown',
|
|
183
|
+
query: i.message || i.description,
|
|
184
|
+
}));
|
|
185
|
+
issues.push(...missingIndexIssues);
|
|
186
|
+
}
|
|
187
|
+
// Détecter les composants déjà lazy-loadés
|
|
188
|
+
metrics.lazyComponents = await this.findLazyComponents(projectPath);
|
|
189
|
+
const duration = Math.round(performance.now() - startTime);
|
|
190
|
+
this.logger.info(`Performance analysis completed in ${duration}ms (${issues.length} issues found)`);
|
|
191
|
+
return { metrics, issues };
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Analyse la taille du bundle
|
|
195
|
+
*/
|
|
196
|
+
async analyzeBundleSize(projectPath) {
|
|
197
|
+
let js = 0;
|
|
198
|
+
let css = 0;
|
|
199
|
+
// Chercher les dossiers de build courants
|
|
200
|
+
const buildDirs = ['dist', 'build', '.next', 'out', '.output'];
|
|
201
|
+
for (const buildDir of buildDirs) {
|
|
202
|
+
const dirPath = join(projectPath, buildDir);
|
|
203
|
+
try {
|
|
204
|
+
await access(dirPath, constants.F_OK);
|
|
205
|
+
const sizes = await this.calculateDirSize(dirPath);
|
|
206
|
+
js += sizes.js;
|
|
207
|
+
css += sizes.css;
|
|
208
|
+
}
|
|
209
|
+
catch {
|
|
210
|
+
// Dossier n'existe pas
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
// Fallback: utiliser next build ou webpack-bundle-analyzer si disponible
|
|
214
|
+
if (js === 0 && css === 0) {
|
|
215
|
+
const estimated = await this.estimateBundleSizeFromDeps(projectPath);
|
|
216
|
+
return { js: estimated, css: 0, total: estimated };
|
|
217
|
+
}
|
|
218
|
+
return { js, css, total: js + css };
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Calcule la taille d'un dossier
|
|
222
|
+
*/
|
|
223
|
+
async calculateDirSize(dirPath) {
|
|
224
|
+
let js = 0;
|
|
225
|
+
let css = 0;
|
|
226
|
+
const traverse = async (path) => {
|
|
227
|
+
try {
|
|
228
|
+
const entries = await readdir(path, { withFileTypes: true });
|
|
229
|
+
for (const entry of entries) {
|
|
230
|
+
const fullPath = join(path, entry.name);
|
|
231
|
+
if (entry.isDirectory()) {
|
|
232
|
+
await traverse(fullPath);
|
|
233
|
+
}
|
|
234
|
+
else if (entry.isFile()) {
|
|
235
|
+
const ext = entry.name.split('.').pop()?.toLowerCase();
|
|
236
|
+
if (ext === 'js' || ext === 'mjs') {
|
|
237
|
+
try {
|
|
238
|
+
const { stat } = await import('node:fs/promises');
|
|
239
|
+
const stats = await stat(fullPath);
|
|
240
|
+
js += stats.size;
|
|
241
|
+
}
|
|
242
|
+
catch {
|
|
243
|
+
// Ignore stat errors
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
else if (ext === 'css') {
|
|
247
|
+
try {
|
|
248
|
+
const { stat } = await import('node:fs/promises');
|
|
249
|
+
const stats = await stat(fullPath);
|
|
250
|
+
css += stats.size;
|
|
251
|
+
}
|
|
252
|
+
catch {
|
|
253
|
+
// Ignore stat errors
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
catch {
|
|
260
|
+
// Ignore errors
|
|
261
|
+
}
|
|
262
|
+
};
|
|
263
|
+
await traverse(dirPath);
|
|
264
|
+
return { js, css };
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Estime la taille du bundle à partir des dépendances
|
|
268
|
+
*/
|
|
269
|
+
async estimateBundleSizeFromDeps(projectPath) {
|
|
270
|
+
try {
|
|
271
|
+
const pkgPath = join(projectPath, 'package.json');
|
|
272
|
+
const content = await readFile(pkgPath, 'utf-8');
|
|
273
|
+
const pkg = JSON.parse(content);
|
|
274
|
+
// Estimations approximatives de tailles de packages populaires
|
|
275
|
+
const packageSizes = {
|
|
276
|
+
react: 45000,
|
|
277
|
+
'react-dom': 130000,
|
|
278
|
+
'react-router-dom': 70000,
|
|
279
|
+
'@tanstack/react-query': 55000,
|
|
280
|
+
axios: 35000,
|
|
281
|
+
lodash: 70000,
|
|
282
|
+
'lodash-es': 60000,
|
|
283
|
+
moment: 70000,
|
|
284
|
+
'dayjs': 2000,
|
|
285
|
+
datefns: 28000,
|
|
286
|
+
'@mui/material': 400000,
|
|
287
|
+
'@mui/system': 180000,
|
|
288
|
+
'@chakra-ui/react': 250000,
|
|
289
|
+
framerMotion: 90000,
|
|
290
|
+
};
|
|
291
|
+
let total = 0;
|
|
292
|
+
const deps = { ...(pkg.dependencies ?? {}), ...(pkg.devDependencies ?? {}) };
|
|
293
|
+
for (const [name, version] of Object.entries(deps)) {
|
|
294
|
+
// Ignorer les @types
|
|
295
|
+
if (name.startsWith('@types/')) {
|
|
296
|
+
continue;
|
|
297
|
+
}
|
|
298
|
+
const baseName = name.split('/').pop() ?? name;
|
|
299
|
+
if (baseName in packageSizes) {
|
|
300
|
+
total += packageSizes[baseName];
|
|
301
|
+
}
|
|
302
|
+
else {
|
|
303
|
+
// Estimation par défaut
|
|
304
|
+
total += 10000;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
return total;
|
|
308
|
+
}
|
|
309
|
+
catch {
|
|
310
|
+
return 0;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* Détecte les opportunités de lazy loading
|
|
315
|
+
*/
|
|
316
|
+
async detectLazyLoading(projectPath) {
|
|
317
|
+
const issues = [];
|
|
318
|
+
const files = await this.findSourceFiles(projectPath);
|
|
319
|
+
for (const filePath of files) {
|
|
320
|
+
try {
|
|
321
|
+
const content = await readFile(filePath, 'utf-8');
|
|
322
|
+
const lines = content.split('\n');
|
|
323
|
+
const relativePath = relative(projectPath, filePath);
|
|
324
|
+
for (const patternConfig of LAZY_LOAD_PATTERNS) {
|
|
325
|
+
let match;
|
|
326
|
+
const regex = new RegExp(patternConfig.pattern.source, patternConfig.pattern.flags);
|
|
327
|
+
while ((match = regex.exec(content)) !== null) {
|
|
328
|
+
const position = this.findPosition(content, match.index);
|
|
329
|
+
// Ignorer si déjà dans un lazy context
|
|
330
|
+
const lineContent = lines[position.line - 1] ?? '';
|
|
331
|
+
if (lineContent.includes('dynamic(') || lineContent.includes('React.lazy(')) {
|
|
332
|
+
continue;
|
|
333
|
+
}
|
|
334
|
+
issues.push({
|
|
335
|
+
id: this.generateIssueId(`lazy-${position.line}`),
|
|
336
|
+
category: 'performance',
|
|
337
|
+
severity: 'medium',
|
|
338
|
+
description: patternConfig.description,
|
|
339
|
+
message: patternConfig.suggestion,
|
|
340
|
+
location: {
|
|
341
|
+
file: filePath,
|
|
342
|
+
line: position.line,
|
|
343
|
+
column: position.column,
|
|
344
|
+
},
|
|
345
|
+
code: this.extractCodeSnippet(lines, position.line, 1),
|
|
346
|
+
fixable: true,
|
|
347
|
+
effort: 3,
|
|
348
|
+
suggestions: [patternConfig.suggestion],
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
catch {
|
|
354
|
+
// Erreur de lecture, ignorer
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
return issues;
|
|
358
|
+
}
|
|
359
|
+
/**
|
|
360
|
+
* Détecte les re-renders inutiles React
|
|
361
|
+
*/
|
|
362
|
+
async detectRerenders(projectPath) {
|
|
363
|
+
const issues = [];
|
|
364
|
+
const files = await this.findSourceFiles(projectPath);
|
|
365
|
+
// Ne traiter que les fichiers React
|
|
366
|
+
const reactFiles = files.filter((f) => /\.(tsx|jsx|vue)$/.test(f));
|
|
367
|
+
for (const filePath of reactFiles) {
|
|
368
|
+
try {
|
|
369
|
+
const content = await readFile(filePath, 'utf-8');
|
|
370
|
+
const lines = content.split('\n');
|
|
371
|
+
const relativePath = relative(projectPath, filePath);
|
|
372
|
+
for (const patternConfig of RE_RENDER_PATTERNS) {
|
|
373
|
+
let match;
|
|
374
|
+
const regex = new RegExp(patternConfig.pattern.source, patternConfig.pattern.flags);
|
|
375
|
+
while ((match = regex.exec(content)) !== null) {
|
|
376
|
+
const position = this.findPosition(content, match.index);
|
|
377
|
+
issues.push({
|
|
378
|
+
id: this.generateIssueId(`rerender-${relativePath}-${position.line}`),
|
|
379
|
+
category: 'performance',
|
|
380
|
+
severity: 'low',
|
|
381
|
+
description: patternConfig.description,
|
|
382
|
+
message: patternConfig.suggestion,
|
|
383
|
+
location: {
|
|
384
|
+
file: filePath,
|
|
385
|
+
line: position.line,
|
|
386
|
+
column: position.column,
|
|
387
|
+
},
|
|
388
|
+
code: this.extractCodeSnippet(lines, position.line, 2),
|
|
389
|
+
fixable: true,
|
|
390
|
+
effort: 4,
|
|
391
|
+
suggestions: [patternConfig.suggestion],
|
|
392
|
+
});
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
catch {
|
|
397
|
+
// Erreur de lecture, ignorer
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
return issues;
|
|
401
|
+
}
|
|
402
|
+
/**
|
|
403
|
+
* Détecte les requêtes N+1
|
|
404
|
+
*/
|
|
405
|
+
async detectNPlusOneQueries(projectPath) {
|
|
406
|
+
const issues = [];
|
|
407
|
+
const files = await this.findSourceFiles(projectPath);
|
|
408
|
+
// Filtrer les fichiers backend/API
|
|
409
|
+
const apiFiles = files.filter((f) => f.includes('/api/') ||
|
|
410
|
+
f.includes('/server/') ||
|
|
411
|
+
f.includes('/controllers/') ||
|
|
412
|
+
f.includes('/services/') ||
|
|
413
|
+
f.includes('/lib/db') ||
|
|
414
|
+
f.endsWith('.query.ts') ||
|
|
415
|
+
f.endsWith('.queries.ts'));
|
|
416
|
+
for (const filePath of apiFiles) {
|
|
417
|
+
try {
|
|
418
|
+
const content = await readFile(filePath, 'utf-8');
|
|
419
|
+
const lines = content.split('\n');
|
|
420
|
+
for (const patternConfig of N_PLUS_ONE_PATTERNS) {
|
|
421
|
+
let match;
|
|
422
|
+
const regex = new RegExp(patternConfig.pattern.source, patternConfig.pattern.flags);
|
|
423
|
+
while ((match = regex.exec(content)) !== null) {
|
|
424
|
+
const position = this.findPosition(content, match.index);
|
|
425
|
+
issues.push({
|
|
426
|
+
id: this.generateIssueId(`nplusone-${filePath.replace(/[^a-zA-Z0-9]/g, '-')}-${position.line}`),
|
|
427
|
+
category: 'performance',
|
|
428
|
+
severity: 'high',
|
|
429
|
+
description: patternConfig.description,
|
|
430
|
+
message: patternConfig.suggestion,
|
|
431
|
+
location: {
|
|
432
|
+
file: filePath,
|
|
433
|
+
line: position.line,
|
|
434
|
+
column: position.column,
|
|
435
|
+
},
|
|
436
|
+
code: this.extractCodeSnippet(lines, position.line, 3),
|
|
437
|
+
fixable: false,
|
|
438
|
+
effort: 6,
|
|
439
|
+
suggestions: [
|
|
440
|
+
patternConfig.suggestion,
|
|
441
|
+
'Use DataLoader pattern',
|
|
442
|
+
'Use JOIN or IN clause for batch queries',
|
|
443
|
+
],
|
|
444
|
+
});
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
catch {
|
|
449
|
+
// Erreur de lecture, ignorer
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
return issues;
|
|
453
|
+
}
|
|
454
|
+
/**
|
|
455
|
+
* Détecte les index manquants (pour Prisma/TypeORM)
|
|
456
|
+
*/
|
|
457
|
+
async detectMissingIndexes(projectPath) {
|
|
458
|
+
const issues = [];
|
|
459
|
+
// Chercher les fichiers Prisma schema
|
|
460
|
+
const schemaFiles = ['schema.prisma', 'prisma/schema.prisma'];
|
|
461
|
+
for (const schemaFile of schemaFiles) {
|
|
462
|
+
const schemaPath = join(projectPath, schemaFile);
|
|
463
|
+
try {
|
|
464
|
+
await access(schemaPath, constants.F_OK);
|
|
465
|
+
const content = await readFile(schemaPath, 'utf-8');
|
|
466
|
+
// Détecter les relations sans index
|
|
467
|
+
const relationPattern = /(\w+)\s+(\w+)\s+@relation\([^)]*\)/g;
|
|
468
|
+
let match;
|
|
469
|
+
while ((match = relationPattern.exec(content)) !== null) {
|
|
470
|
+
const fieldName = match[2];
|
|
471
|
+
const lines = content.split('\n');
|
|
472
|
+
const lineNum = content.substring(0, match.index).split('\n').length;
|
|
473
|
+
// Vérifier s'il y a un index sur ce champ
|
|
474
|
+
const hasIndex = /@@index.*?\[.*?['"`]/.test(content) && content.includes(fieldName);
|
|
475
|
+
const hasUnique = content.includes(`@@unique([${fieldName}]`);
|
|
476
|
+
const hasIdIndex = fieldName === 'id' || fieldName.endsWith('Id');
|
|
477
|
+
if (!hasIndex && !hasUnique && !hasIdIndex) {
|
|
478
|
+
issues.push({
|
|
479
|
+
id: this.generateIssueId(`index-${schemaFile}-${fieldName}`),
|
|
480
|
+
category: 'performance',
|
|
481
|
+
severity: 'medium',
|
|
482
|
+
description: `Missing index on foreign key: ${fieldName}`,
|
|
483
|
+
message: `Foreign key field ${fieldName} should be indexed for better query performance.`,
|
|
484
|
+
location: {
|
|
485
|
+
file: schemaPath,
|
|
486
|
+
line: lineNum,
|
|
487
|
+
},
|
|
488
|
+
code: lines[lineNum - 1],
|
|
489
|
+
fixable: true,
|
|
490
|
+
effort: 2,
|
|
491
|
+
suggestions: [`Add @@index([${fieldName}]) to the model`],
|
|
492
|
+
});
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
catch {
|
|
497
|
+
// Schéma non trouvé
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
return issues;
|
|
501
|
+
}
|
|
502
|
+
/**
|
|
503
|
+
* Trouve les composants déjà lazy-loadés
|
|
504
|
+
*/
|
|
505
|
+
async findLazyComponents(projectPath) {
|
|
506
|
+
const lazy = [];
|
|
507
|
+
const files = await this.findSourceFiles(projectPath);
|
|
508
|
+
for (const filePath of files) {
|
|
509
|
+
try {
|
|
510
|
+
const content = await readFile(filePath, 'utf-8');
|
|
511
|
+
if (/dynamic\s*\(|React\.lazy\s*\(|lazy\s*\(\s*\(|import\s*\(/.test(content)) {
|
|
512
|
+
lazy.push(relative(projectPath, filePath));
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
catch {
|
|
516
|
+
// Erreur de lecture
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
return lazy;
|
|
520
|
+
}
|
|
521
|
+
/**
|
|
522
|
+
* Trouve tous les fichiers source
|
|
523
|
+
*/
|
|
524
|
+
async findSourceFiles(projectPath) {
|
|
525
|
+
const files = [];
|
|
526
|
+
const traverse = async (dir) => {
|
|
527
|
+
try {
|
|
528
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
529
|
+
for (const entry of entries) {
|
|
530
|
+
const fullPath = join(dir, entry.name);
|
|
531
|
+
if (EXCLUDE_DIRS.includes(entry.name)) {
|
|
532
|
+
continue;
|
|
533
|
+
}
|
|
534
|
+
if (entry.isDirectory()) {
|
|
535
|
+
await traverse(fullPath);
|
|
536
|
+
}
|
|
537
|
+
else if (entry.isFile()) {
|
|
538
|
+
const ext = `.${entry.name.split('.').pop()}`;
|
|
539
|
+
if (SOURCE_EXTENSIONS.includes(ext)) {
|
|
540
|
+
files.push(fullPath);
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
catch {
|
|
546
|
+
// Ignorer les erreurs
|
|
547
|
+
}
|
|
548
|
+
};
|
|
549
|
+
await traverse(projectPath);
|
|
550
|
+
return files;
|
|
551
|
+
}
|
|
552
|
+
/**
|
|
553
|
+
* Trouve la position ligne/colonne à partir d'un index
|
|
554
|
+
*/
|
|
555
|
+
findPosition(content, index) {
|
|
556
|
+
const before = content.substring(0, index);
|
|
557
|
+
const lines = before.split('\n');
|
|
558
|
+
return {
|
|
559
|
+
line: lines.length,
|
|
560
|
+
column: (lines[lines.length - 1] ?? '').length + 1,
|
|
561
|
+
};
|
|
562
|
+
}
|
|
563
|
+
/**
|
|
564
|
+
* Extrait un extrait de code
|
|
565
|
+
*/
|
|
566
|
+
extractCodeSnippet(lines, targetLine, context) {
|
|
567
|
+
const start = Math.max(0, targetLine - context - 1);
|
|
568
|
+
const end = Math.min(lines.length, targetLine + context);
|
|
569
|
+
return lines.slice(start, end).join('\n');
|
|
570
|
+
}
|
|
571
|
+
/**
|
|
572
|
+
* Formate une taille en octets pour l'affichage
|
|
573
|
+
*/
|
|
574
|
+
formatBytes(bytes) {
|
|
575
|
+
if (bytes === 0)
|
|
576
|
+
return '0 B';
|
|
577
|
+
const k = 1024;
|
|
578
|
+
const sizes = ['B', 'KB', 'MB', 'GB'];
|
|
579
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
580
|
+
return `${parseFloat((bytes / Math.pow(k, i)).toFixed(2))} ${sizes[i]}`;
|
|
581
|
+
}
|
|
582
|
+
/**
|
|
583
|
+
* Génère un ID unique
|
|
584
|
+
*/
|
|
585
|
+
generateIssueId(suffix) {
|
|
586
|
+
return `perf-${suffix}-${++this.issueCounter}`.replace(/[^a-zA-Z0-9-]/g, '-');
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
//# sourceMappingURL=performance-analyzer.js.map
|