@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.
Files changed (286) hide show
  1. package/README.md +268 -58
  2. package/bin/Dockerfile +158 -16
  3. package/bin/docker-entrypoint.sh +293 -0
  4. package/dist/cli/cli.d.ts.map +1 -1
  5. package/dist/cli/cli.js +22 -2
  6. package/dist/cli/cli.js.map +1 -1
  7. package/dist/cli/commands/command.types.d.ts +216 -0
  8. package/dist/cli/commands/command.types.d.ts.map +1 -0
  9. package/dist/cli/commands/command.types.js +64 -0
  10. package/dist/cli/commands/command.types.js.map +1 -0
  11. package/dist/cli/commands/history.command.d.ts +91 -0
  12. package/dist/cli/commands/history.command.d.ts.map +1 -0
  13. package/dist/cli/commands/history.command.js +336 -0
  14. package/dist/cli/commands/history.command.js.map +1 -0
  15. package/dist/cli/commands/index.d.ts +14 -3
  16. package/dist/cli/commands/index.d.ts.map +1 -1
  17. package/dist/cli/commands/index.js +7 -0
  18. package/dist/cli/commands/index.js.map +1 -1
  19. package/dist/cli/commands/optimize.command.d.ts +110 -0
  20. package/dist/cli/commands/optimize.command.d.ts.map +1 -0
  21. package/dist/cli/commands/optimize.command.js +497 -0
  22. package/dist/cli/commands/optimize.command.js.map +1 -0
  23. package/dist/cli/commands/report.command.d.ts +110 -0
  24. package/dist/cli/commands/report.command.d.ts.map +1 -0
  25. package/dist/cli/commands/report.command.js +532 -0
  26. package/dist/cli/commands/report.command.js.map +1 -0
  27. package/dist/cli/commands/review.command.d.ts +110 -0
  28. package/dist/cli/commands/review.command.d.ts.map +1 -0
  29. package/dist/cli/commands/review.command.js +520 -0
  30. package/dist/cli/commands/review.command.js.map +1 -0
  31. package/dist/cli/commands/score.command.d.ts +47 -0
  32. package/dist/cli/commands/score.command.d.ts.map +1 -0
  33. package/dist/cli/commands/score.command.js +261 -0
  34. package/dist/cli/commands/score.command.js.map +1 -0
  35. package/dist/cli/utils/index.d.ts +10 -0
  36. package/dist/cli/utils/index.d.ts.map +1 -0
  37. package/dist/cli/utils/index.js +10 -0
  38. package/dist/cli/utils/index.js.map +1 -0
  39. package/dist/cli/utils/output.d.ts +192 -0
  40. package/dist/cli/utils/output.d.ts.map +1 -0
  41. package/dist/cli/utils/output.js +411 -0
  42. package/dist/cli/utils/output.js.map +1 -0
  43. package/dist/cli/utils/progress.d.ts +204 -0
  44. package/dist/cli/utils/progress.d.ts.map +1 -0
  45. package/dist/cli/utils/progress.js +396 -0
  46. package/dist/cli/utils/progress.js.map +1 -0
  47. package/dist/core/types/index.d.ts +1 -0
  48. package/dist/core/types/index.d.ts.map +1 -1
  49. package/dist/core/types/project.types.d.ts +3 -3
  50. package/dist/core/types/project.types.d.ts.map +1 -1
  51. package/dist/core/types/scoring.types.d.ts +301 -0
  52. package/dist/core/types/scoring.types.d.ts.map +1 -0
  53. package/dist/core/types/scoring.types.js +8 -0
  54. package/dist/core/types/scoring.types.js.map +1 -0
  55. package/dist/services/detection/framework-detector.d.ts.map +1 -1
  56. package/dist/services/detection/framework-detector.js +74 -5
  57. package/dist/services/detection/framework-detector.js.map +1 -1
  58. package/dist/services/index.d.ts +12 -0
  59. package/dist/services/index.d.ts.map +1 -1
  60. package/dist/services/index.js +14 -0
  61. package/dist/services/index.js.map +1 -1
  62. package/dist/services/optimization/detectors/bug-detector.d.ts +82 -0
  63. package/dist/services/optimization/detectors/bug-detector.d.ts.map +1 -0
  64. package/dist/services/optimization/detectors/bug-detector.js +443 -0
  65. package/dist/services/optimization/detectors/bug-detector.js.map +1 -0
  66. package/dist/services/optimization/detectors/code-smell-detector.d.ts +108 -0
  67. package/dist/services/optimization/detectors/code-smell-detector.d.ts.map +1 -0
  68. package/dist/services/optimization/detectors/code-smell-detector.js +569 -0
  69. package/dist/services/optimization/detectors/code-smell-detector.js.map +1 -0
  70. package/dist/services/optimization/detectors/index.d.ts +7 -0
  71. package/dist/services/optimization/detectors/index.d.ts.map +1 -0
  72. package/dist/services/optimization/detectors/index.js +7 -0
  73. package/dist/services/optimization/detectors/index.js.map +1 -0
  74. package/dist/services/optimization/detectors/perf-detector.d.ts +80 -0
  75. package/dist/services/optimization/detectors/perf-detector.d.ts.map +1 -0
  76. package/dist/services/optimization/detectors/perf-detector.js +451 -0
  77. package/dist/services/optimization/detectors/perf-detector.js.map +1 -0
  78. package/dist/services/optimization/index.d.ts +61 -0
  79. package/dist/services/optimization/index.d.ts.map +1 -0
  80. package/dist/services/optimization/index.js +69 -0
  81. package/dist/services/optimization/index.js.map +1 -0
  82. package/dist/services/optimization/optimization.service.d.ts +65 -0
  83. package/dist/services/optimization/optimization.service.d.ts.map +1 -0
  84. package/dist/services/optimization/optimization.service.js +511 -0
  85. package/dist/services/optimization/optimization.service.js.map +1 -0
  86. package/dist/services/optimization/optimization.types.d.ts +343 -0
  87. package/dist/services/optimization/optimization.types.d.ts.map +1 -0
  88. package/dist/services/optimization/optimization.types.js +8 -0
  89. package/dist/services/optimization/optimization.types.js.map +1 -0
  90. package/dist/services/optimization/optimizers/code-optimizer.d.ts +87 -0
  91. package/dist/services/optimization/optimizers/code-optimizer.d.ts.map +1 -0
  92. package/dist/services/optimization/optimizers/code-optimizer.js +436 -0
  93. package/dist/services/optimization/optimizers/code-optimizer.js.map +1 -0
  94. package/dist/services/optimization/optimizers/index.d.ts +7 -0
  95. package/dist/services/optimization/optimizers/index.d.ts.map +1 -0
  96. package/dist/services/optimization/optimizers/index.js +7 -0
  97. package/dist/services/optimization/optimizers/index.js.map +1 -0
  98. package/dist/services/optimization/optimizers/perf-optimizer.d.ts +64 -0
  99. package/dist/services/optimization/optimizers/perf-optimizer.d.ts.map +1 -0
  100. package/dist/services/optimization/optimizers/perf-optimizer.js +330 -0
  101. package/dist/services/optimization/optimizers/perf-optimizer.js.map +1 -0
  102. package/dist/services/optimization/optimizers/refact-optimizer.d.ts +82 -0
  103. package/dist/services/optimization/optimizers/refact-optimizer.d.ts.map +1 -0
  104. package/dist/services/optimization/optimizers/refact-optimizer.js +354 -0
  105. package/dist/services/optimization/optimizers/refact-optimizer.js.map +1 -0
  106. package/dist/services/optimization/patterns/anti-patterns.d.ts +31 -0
  107. package/dist/services/optimization/patterns/anti-patterns.d.ts.map +1 -0
  108. package/dist/services/optimization/patterns/anti-patterns.js +501 -0
  109. package/dist/services/optimization/patterns/anti-patterns.js.map +1 -0
  110. package/dist/services/optimization/patterns/index.d.ts +5 -0
  111. package/dist/services/optimization/patterns/index.d.ts.map +1 -0
  112. package/dist/services/optimization/patterns/index.js +5 -0
  113. package/dist/services/optimization/patterns/index.js.map +1 -0
  114. package/dist/services/reporting/export/chart.exporter.d.ts +59 -0
  115. package/dist/services/reporting/export/chart.exporter.d.ts.map +1 -0
  116. package/dist/services/reporting/export/chart.exporter.js +350 -0
  117. package/dist/services/reporting/export/chart.exporter.js.map +1 -0
  118. package/dist/services/reporting/export/index.d.ts +9 -0
  119. package/dist/services/reporting/export/index.d.ts.map +1 -0
  120. package/dist/services/reporting/export/index.js +10 -0
  121. package/dist/services/reporting/export/index.js.map +1 -0
  122. package/dist/services/reporting/export/pdf.exporter.d.ts +133 -0
  123. package/dist/services/reporting/export/pdf.exporter.d.ts.map +1 -0
  124. package/dist/services/reporting/export/pdf.exporter.js +270 -0
  125. package/dist/services/reporting/export/pdf.exporter.js.map +1 -0
  126. package/dist/services/reporting/history.service.d.ts +93 -0
  127. package/dist/services/reporting/history.service.d.ts.map +1 -0
  128. package/dist/services/reporting/history.service.js +285 -0
  129. package/dist/services/reporting/history.service.js.map +1 -0
  130. package/dist/services/reporting/index.d.ts +15 -0
  131. package/dist/services/reporting/index.d.ts.map +1 -0
  132. package/dist/services/reporting/index.js +16 -0
  133. package/dist/services/reporting/index.js.map +1 -0
  134. package/dist/services/reporting/report.service.d.ts +102 -0
  135. package/dist/services/reporting/report.service.d.ts.map +1 -0
  136. package/dist/services/reporting/report.service.js +240 -0
  137. package/dist/services/reporting/report.service.js.map +1 -0
  138. package/dist/services/reporting/reporting.types.d.ts +329 -0
  139. package/dist/services/reporting/reporting.types.d.ts.map +1 -0
  140. package/dist/services/reporting/reporting.types.js +8 -0
  141. package/dist/services/reporting/reporting.types.js.map +1 -0
  142. package/dist/services/reporting/templates/html.template.d.ts +81 -0
  143. package/dist/services/reporting/templates/html.template.d.ts.map +1 -0
  144. package/dist/services/reporting/templates/html.template.js +741 -0
  145. package/dist/services/reporting/templates/html.template.js.map +1 -0
  146. package/dist/services/reporting/templates/json.template.d.ts +85 -0
  147. package/dist/services/reporting/templates/json.template.d.ts.map +1 -0
  148. package/dist/services/reporting/templates/json.template.js +308 -0
  149. package/dist/services/reporting/templates/json.template.js.map +1 -0
  150. package/dist/services/reporting/templates/markdown.template.d.ts +69 -0
  151. package/dist/services/reporting/templates/markdown.template.d.ts.map +1 -0
  152. package/dist/services/reporting/templates/markdown.template.js +311 -0
  153. package/dist/services/reporting/templates/markdown.template.js.map +1 -0
  154. package/dist/services/reporting/trend-analyzer.d.ts +73 -0
  155. package/dist/services/reporting/trend-analyzer.d.ts.map +1 -0
  156. package/dist/services/reporting/trend-analyzer.js +291 -0
  157. package/dist/services/reporting/trend-analyzer.js.map +1 -0
  158. package/dist/services/review/analyzers/dependency-analyzer.d.ts +87 -0
  159. package/dist/services/review/analyzers/dependency-analyzer.d.ts.map +1 -0
  160. package/dist/services/review/analyzers/dependency-analyzer.js +458 -0
  161. package/dist/services/review/analyzers/dependency-analyzer.js.map +1 -0
  162. package/dist/services/review/analyzers/index.d.ts +13 -0
  163. package/dist/services/review/analyzers/index.d.ts.map +1 -0
  164. package/dist/services/review/analyzers/index.js +13 -0
  165. package/dist/services/review/analyzers/index.js.map +1 -0
  166. package/dist/services/review/analyzers/nestjs-analyzer.d.ts +210 -0
  167. package/dist/services/review/analyzers/nestjs-analyzer.d.ts.map +1 -0
  168. package/dist/services/review/analyzers/nestjs-analyzer.js +571 -0
  169. package/dist/services/review/analyzers/nestjs-analyzer.js.map +1 -0
  170. package/dist/services/review/analyzers/performance-analyzer.d.ts +91 -0
  171. package/dist/services/review/analyzers/performance-analyzer.d.ts.map +1 -0
  172. package/dist/services/review/analyzers/performance-analyzer.js +589 -0
  173. package/dist/services/review/analyzers/performance-analyzer.js.map +1 -0
  174. package/dist/services/review/analyzers/security-analyzer.d.ts +96 -0
  175. package/dist/services/review/analyzers/security-analyzer.d.ts.map +1 -0
  176. package/dist/services/review/analyzers/security-analyzer.js +512 -0
  177. package/dist/services/review/analyzers/security-analyzer.js.map +1 -0
  178. package/dist/services/review/analyzers/static-analyzer.d.ts +90 -0
  179. package/dist/services/review/analyzers/static-analyzer.d.ts.map +1 -0
  180. package/dist/services/review/analyzers/static-analyzer.js +423 -0
  181. package/dist/services/review/analyzers/static-analyzer.js.map +1 -0
  182. package/dist/services/review/fixers/auto-fixer.d.ts +94 -0
  183. package/dist/services/review/fixers/auto-fixer.d.ts.map +1 -0
  184. package/dist/services/review/fixers/auto-fixer.js +404 -0
  185. package/dist/services/review/fixers/auto-fixer.js.map +1 -0
  186. package/dist/services/review/fixers/index.d.ts +11 -0
  187. package/dist/services/review/fixers/index.d.ts.map +1 -0
  188. package/dist/services/review/fixers/index.js +11 -0
  189. package/dist/services/review/fixers/index.js.map +1 -0
  190. package/dist/services/review/fixers/refactor-suggester.d.ts +100 -0
  191. package/dist/services/review/fixers/refactor-suggester.d.ts.map +1 -0
  192. package/dist/services/review/fixers/refactor-suggester.js +555 -0
  193. package/dist/services/review/fixers/refactor-suggester.js.map +1 -0
  194. package/dist/services/review/fixers/test-generator.d.ts +99 -0
  195. package/dist/services/review/fixers/test-generator.d.ts.map +1 -0
  196. package/dist/services/review/fixers/test-generator.js +458 -0
  197. package/dist/services/review/fixers/test-generator.js.map +1 -0
  198. package/dist/services/review/index.d.ts +14 -0
  199. package/dist/services/review/index.d.ts.map +1 -0
  200. package/dist/services/review/index.js +14 -0
  201. package/dist/services/review/index.js.map +1 -0
  202. package/dist/services/review/reporters/fix-reporter.d.ts +67 -0
  203. package/dist/services/review/reporters/fix-reporter.d.ts.map +1 -0
  204. package/dist/services/review/reporters/fix-reporter.js +437 -0
  205. package/dist/services/review/reporters/fix-reporter.js.map +1 -0
  206. package/dist/services/review/reporters/index.d.ts +10 -0
  207. package/dist/services/review/reporters/index.d.ts.map +1 -0
  208. package/dist/services/review/reporters/index.js +10 -0
  209. package/dist/services/review/reporters/index.js.map +1 -0
  210. package/dist/services/review/reporters/score-reporter.d.ts +84 -0
  211. package/dist/services/review/reporters/score-reporter.d.ts.map +1 -0
  212. package/dist/services/review/reporters/score-reporter.js +560 -0
  213. package/dist/services/review/reporters/score-reporter.js.map +1 -0
  214. package/dist/services/review/review.service.d.ts +129 -0
  215. package/dist/services/review/review.service.d.ts.map +1 -0
  216. package/dist/services/review/review.service.js +396 -0
  217. package/dist/services/review/review.service.js.map +1 -0
  218. package/dist/services/review/review.types.d.ts +443 -0
  219. package/dist/services/review/review.types.d.ts.map +1 -0
  220. package/dist/services/review/review.types.js +11 -0
  221. package/dist/services/review/review.types.js.map +1 -0
  222. package/dist/services/scoring/dimensions/accessibility.analyzer.d.ts +53 -0
  223. package/dist/services/scoring/dimensions/accessibility.analyzer.d.ts.map +1 -0
  224. package/dist/services/scoring/dimensions/accessibility.analyzer.js +260 -0
  225. package/dist/services/scoring/dimensions/accessibility.analyzer.js.map +1 -0
  226. package/dist/services/scoring/dimensions/backend-logic.analyzer.d.ts +138 -0
  227. package/dist/services/scoring/dimensions/backend-logic.analyzer.d.ts.map +1 -0
  228. package/dist/services/scoring/dimensions/backend-logic.analyzer.js +713 -0
  229. package/dist/services/scoring/dimensions/backend-logic.analyzer.js.map +1 -0
  230. package/dist/services/scoring/dimensions/business-logic.analyzer.d.ts +142 -0
  231. package/dist/services/scoring/dimensions/business-logic.analyzer.d.ts.map +1 -0
  232. package/dist/services/scoring/dimensions/business-logic.analyzer.js +747 -0
  233. package/dist/services/scoring/dimensions/business-logic.analyzer.js.map +1 -0
  234. package/dist/services/scoring/dimensions/code-quality.analyzer.d.ts +142 -0
  235. package/dist/services/scoring/dimensions/code-quality.analyzer.d.ts.map +1 -0
  236. package/dist/services/scoring/dimensions/code-quality.analyzer.js +685 -0
  237. package/dist/services/scoring/dimensions/code-quality.analyzer.js.map +1 -0
  238. package/dist/services/scoring/dimensions/index.d.ts +18 -0
  239. package/dist/services/scoring/dimensions/index.d.ts.map +1 -0
  240. package/dist/services/scoring/dimensions/index.js +27 -0
  241. package/dist/services/scoring/dimensions/index.js.map +1 -0
  242. package/dist/services/scoring/dimensions/performance.analyzer.d.ts +125 -0
  243. package/dist/services/scoring/dimensions/performance.analyzer.d.ts.map +1 -0
  244. package/dist/services/scoring/dimensions/performance.analyzer.js +615 -0
  245. package/dist/services/scoring/dimensions/performance.analyzer.js.map +1 -0
  246. package/dist/services/scoring/dimensions/security.analyzer.d.ts +53 -0
  247. package/dist/services/scoring/dimensions/security.analyzer.d.ts.map +1 -0
  248. package/dist/services/scoring/dimensions/security.analyzer.js +327 -0
  249. package/dist/services/scoring/dimensions/security.analyzer.js.map +1 -0
  250. package/dist/services/scoring/dimensions/seo.analyzer.d.ts +77 -0
  251. package/dist/services/scoring/dimensions/seo.analyzer.d.ts.map +1 -0
  252. package/dist/services/scoring/dimensions/seo.analyzer.js +502 -0
  253. package/dist/services/scoring/dimensions/seo.analyzer.js.map +1 -0
  254. package/dist/services/scoring/dimensions/test-coverage.analyzer.d.ts +106 -0
  255. package/dist/services/scoring/dimensions/test-coverage.analyzer.d.ts.map +1 -0
  256. package/dist/services/scoring/dimensions/test-coverage.analyzer.js +496 -0
  257. package/dist/services/scoring/dimensions/test-coverage.analyzer.js.map +1 -0
  258. package/dist/services/scoring/dimensions/ui-ux.analyzer.d.ts +126 -0
  259. package/dist/services/scoring/dimensions/ui-ux.analyzer.d.ts.map +1 -0
  260. package/dist/services/scoring/dimensions/ui-ux.analyzer.js +665 -0
  261. package/dist/services/scoring/dimensions/ui-ux.analyzer.js.map +1 -0
  262. package/dist/services/scoring/index.d.ts +10 -0
  263. package/dist/services/scoring/index.d.ts.map +1 -0
  264. package/dist/services/scoring/index.js +10 -0
  265. package/dist/services/scoring/index.js.map +1 -0
  266. package/dist/services/scoring/scoring-service.d.ts +222 -0
  267. package/dist/services/scoring/scoring-service.d.ts.map +1 -0
  268. package/dist/services/scoring/scoring-service.js +636 -0
  269. package/dist/services/scoring/scoring-service.js.map +1 -0
  270. package/package.json +13 -4
  271. package/templates/README.md +183 -0
  272. package/templates/nestjs/controller.spec.ts +203 -0
  273. package/templates/nestjs/e2e/api.e2e-spec.ts +451 -0
  274. package/templates/nestjs/e2e/auth.e2e-spec.ts +533 -0
  275. package/templates/nestjs/fixtures/test-module.ts +311 -0
  276. package/templates/nestjs/guard.spec.ts +314 -0
  277. package/templates/nestjs/interceptor.spec.ts +458 -0
  278. package/templates/nestjs/module.spec.ts +173 -0
  279. package/templates/nestjs/pipe.spec.ts +474 -0
  280. package/templates/nestjs/service.spec.ts +296 -0
  281. package/templates/rust/Cargo.toml +72 -0
  282. package/templates/rust/actix-controller.test.rs +114 -0
  283. package/templates/rust/axum-handler.test.rs +117 -0
  284. package/templates/rust/integration.test.rs +63 -0
  285. package/templates/rust/rocket-route.test.rs +106 -0
  286. package/templates/rust/unit.test.rs +38 -0
@@ -0,0 +1,685 @@
1
+ /**
2
+ * Code Quality Analyzer
3
+ *
4
+ * Analyzes code quality by running ESLint, checking cyclomatic complexity,
5
+ * detecting code duplication, and enforcing naming conventions.
6
+ *
7
+ * @module services/scoring/dimensions/code-quality
8
+ */
9
+ import { readFile, access, readdir } from 'node:fs/promises';
10
+ import { join, extname, relative } from 'node:path';
11
+ import { existsSync } from 'node:fs';
12
+ import { CommandExecutor } from '../../../shared/utils/command-executor.js';
13
+ import { createLogger } from '../../../shared/utils/logger.js';
14
+ /**
15
+ * Code Quality Analyzer
16
+ *
17
+ * Evaluates code quality across multiple dimensions:
18
+ * - ESLint violations
19
+ * - Cyclomatic complexity
20
+ * - Code duplication
21
+ * - Function length
22
+ * - Naming conventions
23
+ */
24
+ export class CodeQualityAnalyzer {
25
+ /** Analyzer configuration */
26
+ config = {
27
+ dimension: 'code-quality',
28
+ defaultWeight: 0.15,
29
+ estimatedDuration: 45000,
30
+ supportedFrameworks: ['Next.js', 'React', 'Vue', 'Nuxt', 'Svelte', 'NestJS', 'Angular', 'Remix', 'SvelteKit', 'Astro', 'Gatsby'],
31
+ };
32
+ logger;
33
+ executor;
34
+ maxComplexity;
35
+ maxFunctionLength;
36
+ duplicationThreshold;
37
+ eslintCommand;
38
+ checkTypeScript;
39
+ constructor(options = {}) {
40
+ this.logger = createLogger('CodeQualityAnalyzer');
41
+ this.executor = new CommandExecutor();
42
+ this.maxComplexity = options.maxComplexity ?? 10;
43
+ this.maxFunctionLength = options.maxFunctionLength ?? 50;
44
+ this.duplicationThreshold = options.duplicationThreshold ?? 5;
45
+ this.eslintCommand = options.eslintCommand ?? 'npx eslint';
46
+ this.checkTypeScript = options.checkTypeScript ?? true;
47
+ }
48
+ /**
49
+ * Get the dimension this analyzer handles
50
+ */
51
+ getDimension() {
52
+ return 'code-quality';
53
+ }
54
+ /**
55
+ * Get the default weight for this dimension
56
+ */
57
+ getWeight() {
58
+ return 0.15; // 15% weight in overall score
59
+ }
60
+ /**
61
+ * Analyze code quality for a project
62
+ *
63
+ * @param projectPath - Path to the project root
64
+ * @param _framework - Detected framework (optional, for framework-specific analysis)
65
+ * @param _options - Scoring options (optional)
66
+ * @returns Dimension score with quality metrics
67
+ */
68
+ async analyze(projectPath, _framework, _options) {
69
+ const startTime = performance.now();
70
+ this.logger.info(`Analyzing code quality for: ${projectPath}`);
71
+ const issues = [];
72
+ const improvements = [];
73
+ try {
74
+ // Run ESLint
75
+ const eslintResults = await this.runEslint(projectPath);
76
+ // Analyze complexity
77
+ const complexityMetrics = await this.analyzeComplexity(projectPath);
78
+ // Check for code duplication
79
+ const duplications = await this.detectDuplications(projectPath);
80
+ // Check function lengths
81
+ const longFunctions = await this.findLongFunctions(projectPath);
82
+ // Check naming conventions
83
+ const namingViolations = await this.checkNamingConventions(projectPath);
84
+ // Build issues list
85
+ issues.push(...this.identifyQualityIssues(eslintResults, complexityMetrics, duplications, longFunctions, namingViolations));
86
+ // Build improvements list
87
+ improvements.push(...this.generateQualityImprovements(eslintResults, complexityMetrics, duplications, longFunctions));
88
+ // Calculate final score
89
+ const score = this.calculateQualityScore(eslintResults, complexityMetrics, duplications, longFunctions);
90
+ const duration = Math.round(performance.now() - startTime);
91
+ return {
92
+ dimension: this.getDimension(),
93
+ score,
94
+ weight: this.getWeight(),
95
+ weightedScore: score * this.getWeight(),
96
+ issues,
97
+ improvements,
98
+ metadata: {
99
+ itemsChecked: await this.countSourceFiles(projectPath),
100
+ itemsPassed: await this.countPassedChecks(eslintResults, complexityMetrics, duplications),
101
+ metrics: {
102
+ eslintErrors: eslintResults.reduce((sum, r) => sum + r.errorCount, 0),
103
+ eslintWarnings: eslintResults.reduce((sum, r) => sum + r.warningCount, 0),
104
+ avgComplexity: this.calculateAverageComplexity(complexityMetrics),
105
+ maxComplexity: Math.max(0, ...complexityMetrics.map((m) => m.complexity)),
106
+ duplications: duplications.length,
107
+ longFunctions: longFunctions.length,
108
+ namingViolations: namingViolations.length,
109
+ },
110
+ },
111
+ };
112
+ }
113
+ catch (error) {
114
+ this.logger.error('Error analyzing code quality', error);
115
+ issues.push({
116
+ severity: 'high',
117
+ category: 'code-style',
118
+ description: `Failed to analyze code quality: ${error instanceof Error ? error.message : String(error)}`,
119
+ fixable: false,
120
+ });
121
+ const duration = Math.round(performance.now() - startTime);
122
+ return {
123
+ dimension: this.getDimension(),
124
+ score: 0,
125
+ weight: this.getWeight(),
126
+ weightedScore: 0,
127
+ issues,
128
+ improvements,
129
+ metadata: { error: String(error) },
130
+ };
131
+ }
132
+ }
133
+ /**
134
+ * Run ESLint and parse results
135
+ */
136
+ async runEslint(projectPath) {
137
+ const results = [];
138
+ try {
139
+ // Check for ESLint config
140
+ const hasEslintConfig = await this.hasEslintConfig(projectPath);
141
+ if (!hasEslintConfig) {
142
+ this.logger.info('No ESLint config found, skipping ESLint analysis');
143
+ return results;
144
+ }
145
+ // Run ESLint with JSON formatter
146
+ const command = `${this.eslintCommand} . --format json --max-warnings 0`;
147
+ const execResult = await this.executor.execute(command, {
148
+ cwd: projectPath,
149
+ timeout: 60000,
150
+ silent: true,
151
+ });
152
+ if (!execResult.success || !execResult.data) {
153
+ // ESLint may have found issues, try to parse output
154
+ if (execResult.error) {
155
+ this.logger.warn('ESLint execution failed', execResult.error);
156
+ }
157
+ return results;
158
+ }
159
+ try {
160
+ const eslintOutput = JSON.parse(execResult.data.stdout);
161
+ return Array.isArray(eslintOutput) ? eslintOutput : [eslintOutput];
162
+ }
163
+ catch {
164
+ // Output not in expected format
165
+ return results;
166
+ }
167
+ }
168
+ catch (error) {
169
+ this.logger.warn('Failed to run ESLint', error);
170
+ return results;
171
+ }
172
+ }
173
+ /**
174
+ * Check if project has ESLint configuration
175
+ */
176
+ async hasEslintConfig(projectPath) {
177
+ const configFiles = [
178
+ '.eslintrc.js',
179
+ '.eslintrc.cjs',
180
+ '.eslintrc.json',
181
+ '.eslintrc.yaml',
182
+ '.eslintrc.yml',
183
+ 'eslint.config.js',
184
+ 'eslint.config.mjs',
185
+ '.eslintrc',
186
+ ];
187
+ for (const file of configFiles) {
188
+ try {
189
+ await access(join(projectPath, file));
190
+ return true;
191
+ }
192
+ catch {
193
+ continue;
194
+ }
195
+ }
196
+ // Also check package.json for eslintConfig
197
+ try {
198
+ const pkgPath = join(projectPath, 'package.json');
199
+ const pkgContent = await readFile(pkgPath, 'utf-8');
200
+ const pkg = JSON.parse(pkgContent);
201
+ if (pkg.eslintConfig) {
202
+ return true;
203
+ }
204
+ }
205
+ catch {
206
+ // Ignore
207
+ }
208
+ return false;
209
+ }
210
+ /**
211
+ * Analyze cyclomatic complexity of source files
212
+ */
213
+ async analyzeComplexity(projectPath) {
214
+ const complexities = [];
215
+ const srcDir = join(projectPath, 'src');
216
+ if (!existsSync(srcDir)) {
217
+ return complexities;
218
+ }
219
+ await this.scanDirectoryForComplexity(srcDir, projectPath, complexities);
220
+ return complexities;
221
+ }
222
+ /**
223
+ * Recursively scan directory and calculate complexity
224
+ */
225
+ async scanDirectoryForComplexity(dir, projectPath, complexities) {
226
+ try {
227
+ const entries = await readdir(dir, { withFileTypes: true });
228
+ for (const entry of entries) {
229
+ const fullPath = join(dir, entry.name);
230
+ if (entry.isDirectory()) {
231
+ if (['node_modules', '.git', 'dist', 'build', 'coverage'].includes(entry.name)) {
232
+ continue;
233
+ }
234
+ await this.scanDirectoryForComplexity(fullPath, projectPath, complexities);
235
+ }
236
+ else if (entry.isFile()) {
237
+ const ext = extname(entry.name);
238
+ if (['.ts', '.tsx', '.js', '.jsx'].includes(ext)) {
239
+ const metrics = await this.calculateFileComplexity(fullPath);
240
+ if (metrics) {
241
+ complexities.push(metrics);
242
+ }
243
+ }
244
+ }
245
+ }
246
+ }
247
+ catch {
248
+ // Directory not accessible
249
+ }
250
+ }
251
+ /**
252
+ * Calculate cyclomatic complexity for a single file
253
+ */
254
+ async calculateFileComplexity(filepath) {
255
+ try {
256
+ const content = await readFile(filepath, 'utf-8');
257
+ const lines = content.split('\n');
258
+ // Simplified complexity calculation
259
+ // Count decision points: if, else, for, while, case, catch, ?, &&, ||
260
+ let complexity = 1; // Base complexity
261
+ let functionCount = 0;
262
+ const decisionKeywords = [
263
+ /\bif\b/g,
264
+ /\belse\b/g,
265
+ /\bfor\b/g,
266
+ /\bwhile\b/g,
267
+ /\bswitch\b/g,
268
+ /\bcase\b/g,
269
+ /\bcatch\b/g,
270
+ /\?/g,
271
+ /&&/g,
272
+ /\|\|/g,
273
+ ];
274
+ for (const line of lines) {
275
+ const trimmed = line.trim();
276
+ // Count function declarations
277
+ if (/^\s*(async\s+)?(function\s+\w+|const\s+\w+\s*=\s*(async\s+)?\(?[\w\s,]*\)?\s*=>)/.test(trimmed)) {
278
+ functionCount++;
279
+ }
280
+ // Count decision points
281
+ for (const keyword of decisionKeywords) {
282
+ const matches = trimmed.match(keyword);
283
+ if (matches) {
284
+ complexity += matches.length;
285
+ }
286
+ }
287
+ }
288
+ return {
289
+ filepath: relative(process.cwd(), filepath),
290
+ complexity,
291
+ lineCount: lines.length,
292
+ functionCount,
293
+ };
294
+ }
295
+ catch {
296
+ return null;
297
+ }
298
+ }
299
+ /**
300
+ * Detect code duplication using simplified token analysis
301
+ */
302
+ async detectDuplications(projectPath) {
303
+ const duplications = [];
304
+ const srcDir = join(projectPath, 'src');
305
+ if (!existsSync(srcDir)) {
306
+ return duplications;
307
+ }
308
+ // Collect all normalized lines from source files
309
+ const lineMap = new Map();
310
+ await this.collectNormalizedLines(srcDir, lineMap);
311
+ // Find duplicated lines
312
+ for (const [line, files] of Array.from(lineMap.entries())) {
313
+ if (files.length >= this.duplicationThreshold && line.length > 30) {
314
+ // Skip very short lines (likely not meaningful duplication)
315
+ duplications.push({
316
+ filepath: files[0],
317
+ lines: line,
318
+ occurrences: files.length,
319
+ });
320
+ }
321
+ }
322
+ return duplications.slice(0, 50); // Limit results
323
+ }
324
+ /**
325
+ * Collect normalized lines from source files for duplication detection
326
+ */
327
+ async collectNormalizedLines(dir, lineMap) {
328
+ try {
329
+ const entries = await readdir(dir, { withFileTypes: true });
330
+ for (const entry of entries) {
331
+ const fullPath = join(dir, entry.name);
332
+ if (entry.isDirectory()) {
333
+ if (['node_modules', '.git', 'dist', 'build', 'coverage'].includes(entry.name)) {
334
+ continue;
335
+ }
336
+ await this.collectNormalizedLines(fullPath, lineMap);
337
+ }
338
+ else if (entry.isFile()) {
339
+ const ext = extname(entry.name);
340
+ if (['.ts', '.tsx', '.js', '.jsx'].includes(ext)) {
341
+ try {
342
+ const content = await readFile(fullPath, 'utf-8');
343
+ const lines = content.split('\n');
344
+ for (const line of lines) {
345
+ const trimmed = line.trim();
346
+ // Normalize: remove string literals, extra whitespace
347
+ const normalized = trimmed
348
+ .replace(/['"`].*?['"`]/g, '""')
349
+ .replace(/\s+/g, ' ')
350
+ .toLowerCase();
351
+ if (normalized.length > 20 && !normalized.startsWith('//') && !normalized.startsWith('*')) {
352
+ if (!lineMap.has(normalized)) {
353
+ lineMap.set(normalized, []);
354
+ }
355
+ lineMap.get(normalized).push(relative(process.cwd(), fullPath));
356
+ }
357
+ }
358
+ }
359
+ catch {
360
+ // Skip files that can't be read
361
+ }
362
+ }
363
+ }
364
+ }
365
+ }
366
+ catch {
367
+ // Directory not accessible
368
+ }
369
+ }
370
+ /**
371
+ * Find functions that exceed maximum length
372
+ */
373
+ async findLongFunctions(projectPath) {
374
+ const longFunctions = [];
375
+ const srcDir = join(projectPath, 'src');
376
+ if (!existsSync(srcDir)) {
377
+ return longFunctions;
378
+ }
379
+ await this.scanForLongFunctions(srcDir, projectPath, longFunctions);
380
+ return longFunctions;
381
+ }
382
+ /**
383
+ * Scan directory for long functions
384
+ */
385
+ async scanForLongFunctions(dir, projectPath, longFunctions) {
386
+ try {
387
+ const entries = await readdir(dir, { withFileTypes: true });
388
+ for (const entry of entries) {
389
+ const fullPath = join(dir, entry.name);
390
+ if (entry.isDirectory()) {
391
+ if (['node_modules', '.git', 'dist', 'build', 'coverage'].includes(entry.name)) {
392
+ continue;
393
+ }
394
+ await this.scanForLongFunctions(fullPath, projectPath, longFunctions);
395
+ }
396
+ else if (entry.isFile()) {
397
+ const ext = extname(entry.name);
398
+ if (['.ts', '.tsx', '.js', '.jsx'].includes(ext)) {
399
+ await this.analyzeFileForLongFunctions(fullPath, longFunctions);
400
+ }
401
+ }
402
+ }
403
+ }
404
+ catch {
405
+ // Directory not accessible
406
+ }
407
+ }
408
+ /**
409
+ * Analyze a single file for long functions
410
+ */
411
+ async analyzeFileForLongFunctions(filepath, longFunctions) {
412
+ try {
413
+ const content = await readFile(filepath, 'utf-8');
414
+ const lines = content.split('\n');
415
+ let currentFunction = null;
416
+ for (let i = 0; i < lines.length; i++) {
417
+ const line = lines[i].trim();
418
+ // Detect function start
419
+ const functionMatch = line.match(/(?:function\s+(\w+)|(?:const|let|var)\s+(\w+)\s*=\s*(?:async\s+)?(?:\(?[\w\s,]*\)?\s*=>)|(\w+)\s*\([^)]*\)\s*{)/);
420
+ if (functionMatch && !line.includes('=>')) {
421
+ const functionName = functionMatch[1] || functionMatch[2] || functionMatch[3] || 'anonymous';
422
+ currentFunction = { name: functionName, startLine: i, braceCount: 0 };
423
+ }
424
+ // Track braces for function scope
425
+ if (currentFunction) {
426
+ if (line.includes('{')) {
427
+ currentFunction.braceCount += (line.match(/{/g) || []).length;
428
+ }
429
+ if (line.includes('}')) {
430
+ currentFunction.braceCount -= (line.match(/}/g) || []).length;
431
+ // Function ended
432
+ if (currentFunction.braceCount === 0) {
433
+ const lineCount = i - currentFunction.startLine;
434
+ if (lineCount > this.maxFunctionLength) {
435
+ longFunctions.push({
436
+ filepath: relative(process.cwd(), filepath),
437
+ functionName: currentFunction.name,
438
+ lineCount,
439
+ });
440
+ }
441
+ currentFunction = null;
442
+ }
443
+ }
444
+ }
445
+ }
446
+ }
447
+ catch {
448
+ // Skip files that can't be read
449
+ }
450
+ }
451
+ /**
452
+ * Check naming conventions
453
+ */
454
+ async checkNamingConventions(projectPath) {
455
+ const violations = [];
456
+ const srcDir = join(projectPath, 'src');
457
+ if (!existsSync(srcDir)) {
458
+ return violations;
459
+ }
460
+ await this.scanForNamingViolations(srcDir, violations);
461
+ return violations;
462
+ }
463
+ /**
464
+ * Scan directory for naming convention violations
465
+ */
466
+ async scanForNamingViolations(dir, violations) {
467
+ try {
468
+ const entries = await readdir(dir, { withFileTypes: true });
469
+ for (const entry of entries) {
470
+ const fullPath = join(dir, entry.name);
471
+ if (entry.isDirectory()) {
472
+ if (['node_modules', '.git', 'dist', 'build', 'coverage'].includes(entry.name)) {
473
+ continue;
474
+ }
475
+ await this.scanForNamingViolations(fullPath, violations);
476
+ }
477
+ else if (entry.isFile()) {
478
+ const ext = extname(entry.name);
479
+ // Check file naming
480
+ if (['.ts', '.tsx', '.js', '.jsx'].includes(ext)) {
481
+ const expectedPattern = /^(?:[a-z][a-z0-9-]*|[A-Z][a-zA-Z0-9]*)$/;
482
+ if (!expectedPattern.test(entry.name.replace(ext, '')) && !entry.name.includes('.test.') && !entry.name.includes('.spec.')) {
483
+ violations.push({
484
+ filepath: relative(process.cwd(), fullPath),
485
+ issue: `File name "${entry.name}" does not follow kebab-case or PascalCase conventions`,
486
+ });
487
+ }
488
+ }
489
+ }
490
+ }
491
+ }
492
+ catch {
493
+ // Directory not accessible
494
+ }
495
+ }
496
+ /**
497
+ * Identify quality issues from metrics
498
+ */
499
+ identifyQualityIssues(eslintResults, complexityMetrics, duplications, longFunctions, namingViolations) {
500
+ const issues = [];
501
+ // ESLint issues
502
+ const totalErrors = eslintResults.reduce((sum, r) => sum + r.errorCount, 0);
503
+ const totalWarnings = eslintResults.reduce((sum, r) => sum + r.warningCount, 0);
504
+ if (totalErrors > 0) {
505
+ issues.push({
506
+ severity: 'high',
507
+ category: 'code-style',
508
+ description: `ESLint found ${totalErrors} error(s) and ${totalWarnings} warning(s)`,
509
+ fixable: eslintResults.reduce((sum, r) => sum + r.fixableErrorCount, 0) > 0,
510
+ suggestion: 'Run eslint with --fix to auto-fix some issues',
511
+ });
512
+ }
513
+ // Complexity issues
514
+ const highComplexityFiles = complexityMetrics.filter((m) => m.complexity > this.maxComplexity);
515
+ if (highComplexityFiles.length > 0) {
516
+ issues.push({
517
+ severity: 'medium',
518
+ category: 'code-style',
519
+ description: `${highComplexityFiles.length} file(s) exceed complexity threshold of ${this.maxComplexity}`,
520
+ fixable: false,
521
+ suggestion: 'Refactor complex functions into smaller, more manageable pieces',
522
+ });
523
+ }
524
+ // Duplication issues
525
+ if (duplications.length > 0) {
526
+ issues.push({
527
+ severity: 'medium',
528
+ category: 'code-style',
529
+ description: `Found ${duplications.length} duplicated code pattern(s)`,
530
+ fixable: false,
531
+ suggestion: 'Extract duplicated code into reusable functions or utilities',
532
+ });
533
+ }
534
+ // Long function issues
535
+ if (longFunctions.length > 0) {
536
+ issues.push({
537
+ severity: 'medium',
538
+ category: 'code-style',
539
+ description: `${longFunctions.length} function(s) exceed ${this.maxFunctionLength} lines`,
540
+ fixable: false,
541
+ suggestion: 'Break down long functions into smaller, single-purpose functions',
542
+ });
543
+ }
544
+ // Naming violations
545
+ if (namingViolations.length > 0) {
546
+ issues.push({
547
+ severity: 'low',
548
+ category: 'code-style',
549
+ description: `${namingViolations.length} file(s) have naming convention violations`,
550
+ fixable: true,
551
+ suggestion: 'Rename files to follow kebab-case or PascalCase conventions',
552
+ });
553
+ }
554
+ return issues;
555
+ }
556
+ /**
557
+ * Generate improvement suggestions
558
+ */
559
+ generateQualityImprovements(eslintResults, complexityMetrics, duplications, longFunctions) {
560
+ const improvements = [];
561
+ // ESLint improvements
562
+ if (eslintResults.length > 0) {
563
+ const fixableIssues = eslintResults.reduce((sum, r) => sum + r.fixableErrorCount + r.fixableWarningCount, 0);
564
+ if (fixableIssues > 0) {
565
+ improvements.push({
566
+ type: 'refactor',
567
+ description: `Run eslint --fix to resolve ${fixableIssues} auto-fixable issue(s)`,
568
+ effort: 'quick',
569
+ impact: 'medium',
570
+ });
571
+ }
572
+ }
573
+ // Complexity improvements
574
+ const highComplexityFiles = complexityMetrics.filter((m) => m.complexity > this.maxComplexity);
575
+ if (highComplexityFiles.length > 0) {
576
+ improvements.push({
577
+ type: 'refactor',
578
+ description: `Reduce complexity in ${highComplexityFiles.length} file(s) by extracting complex logic`,
579
+ effort: 'moderate',
580
+ impact: 'high',
581
+ });
582
+ }
583
+ // Duplication improvements
584
+ if (duplications.length > 0) {
585
+ improvements.push({
586
+ type: 'refactor',
587
+ description: `Eliminate ${duplications.length} code duplication pattern(s)`,
588
+ effort: 'moderate',
589
+ impact: 'medium',
590
+ });
591
+ }
592
+ // Long function improvements
593
+ if (longFunctions.length > 0 && longFunctions.length <= 5) {
594
+ improvements.push({
595
+ type: 'refactor',
596
+ description: 'Break down long functions into smaller components',
597
+ effort: 'moderate',
598
+ impact: 'medium',
599
+ steps: [
600
+ 'Identify the core responsibility of each function',
601
+ 'Extract auxiliary logic into separate functions',
602
+ 'Consider using design patterns for complex logic',
603
+ ],
604
+ });
605
+ }
606
+ return improvements;
607
+ }
608
+ /**
609
+ * Calculate final quality score
610
+ */
611
+ calculateQualityScore(eslintResults, complexityMetrics, duplications, longFunctions) {
612
+ let score = 100;
613
+ // ESLint penalty
614
+ const totalErrors = eslintResults.reduce((sum, r) => sum + r.errorCount, 0);
615
+ const totalWarnings = eslintResults.reduce((sum, r) => sum + r.warningCount, 0);
616
+ score -= totalErrors * 2;
617
+ score -= totalWarnings * 0.5;
618
+ // Complexity penalty
619
+ const avgComplexity = this.calculateAverageComplexity(complexityMetrics);
620
+ if (avgComplexity > this.maxComplexity) {
621
+ score -= (avgComplexity - this.maxComplexity) * 3;
622
+ }
623
+ // Duplication penalty
624
+ score -= duplications.length * 2;
625
+ // Long function penalty
626
+ score -= longFunctions.length * 3;
627
+ return Math.max(0, Math.min(100, score));
628
+ }
629
+ /**
630
+ * Calculate average complexity
631
+ */
632
+ calculateAverageComplexity(metrics) {
633
+ if (metrics.length === 0)
634
+ return 0;
635
+ const total = metrics.reduce((sum, m) => sum + m.complexity, 0);
636
+ return total / metrics.length;
637
+ }
638
+ /**
639
+ * Count source files in project
640
+ */
641
+ async countSourceFiles(projectPath) {
642
+ const srcDir = join(projectPath, 'src');
643
+ if (!existsSync(srcDir))
644
+ return 0;
645
+ let count = 0;
646
+ const extensions = ['.ts', '.tsx', '.js', '.jsx'];
647
+ async function countDir(dir) {
648
+ try {
649
+ const entries = await readdir(dir, { withFileTypes: true });
650
+ for (const entry of entries) {
651
+ const fullPath = join(dir, entry.name);
652
+ if (entry.isDirectory()) {
653
+ if (!['node_modules', '.git', 'dist', 'build', 'coverage'].includes(entry.name)) {
654
+ await countDir(fullPath);
655
+ }
656
+ }
657
+ else if (entry.isFile() && extensions.includes(extname(entry.name))) {
658
+ count++;
659
+ }
660
+ }
661
+ }
662
+ catch {
663
+ // Ignore
664
+ }
665
+ }
666
+ await countDir(srcDir);
667
+ return count;
668
+ }
669
+ /**
670
+ * Count passed checks
671
+ */
672
+ async countPassedChecks(eslintResults, complexityMetrics, duplications) {
673
+ const totalErrors = eslintResults.reduce((sum, r) => sum + r.errorCount, 0);
674
+ const highComplexity = complexityMetrics.filter((m) => m.complexity > this.maxComplexity).length;
675
+ const passedIssues = totalErrors === 0 ? 1 : 0;
676
+ const passedComplexity = highComplexity === 0 ? 1 : 0;
677
+ const passedDuplications = duplications.length === 0 ? 1 : 0;
678
+ return passedIssues + passedComplexity + passedDuplications;
679
+ }
680
+ }
681
+ /**
682
+ * Default analyzer instance
683
+ */
684
+ export const codeQualityAnalyzer = new CodeQualityAnalyzer();
685
+ //# sourceMappingURL=code-quality.analyzer.js.map