@oalacea/daemon 0.6.4 → 0.7.0

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 (285) hide show
  1. package/README.md +268 -58
  2. package/bin/Dockerfile +158 -16
  3. package/dist/cli/cli.d.ts.map +1 -1
  4. package/dist/cli/cli.js +22 -2
  5. package/dist/cli/cli.js.map +1 -1
  6. package/dist/cli/commands/command.types.d.ts +216 -0
  7. package/dist/cli/commands/command.types.d.ts.map +1 -0
  8. package/dist/cli/commands/command.types.js +64 -0
  9. package/dist/cli/commands/command.types.js.map +1 -0
  10. package/dist/cli/commands/history.command.d.ts +91 -0
  11. package/dist/cli/commands/history.command.d.ts.map +1 -0
  12. package/dist/cli/commands/history.command.js +336 -0
  13. package/dist/cli/commands/history.command.js.map +1 -0
  14. package/dist/cli/commands/index.d.ts +14 -3
  15. package/dist/cli/commands/index.d.ts.map +1 -1
  16. package/dist/cli/commands/index.js +7 -0
  17. package/dist/cli/commands/index.js.map +1 -1
  18. package/dist/cli/commands/optimize.command.d.ts +110 -0
  19. package/dist/cli/commands/optimize.command.d.ts.map +1 -0
  20. package/dist/cli/commands/optimize.command.js +497 -0
  21. package/dist/cli/commands/optimize.command.js.map +1 -0
  22. package/dist/cli/commands/report.command.d.ts +110 -0
  23. package/dist/cli/commands/report.command.d.ts.map +1 -0
  24. package/dist/cli/commands/report.command.js +532 -0
  25. package/dist/cli/commands/report.command.js.map +1 -0
  26. package/dist/cli/commands/review.command.d.ts +110 -0
  27. package/dist/cli/commands/review.command.d.ts.map +1 -0
  28. package/dist/cli/commands/review.command.js +520 -0
  29. package/dist/cli/commands/review.command.js.map +1 -0
  30. package/dist/cli/commands/score.command.d.ts +47 -0
  31. package/dist/cli/commands/score.command.d.ts.map +1 -0
  32. package/dist/cli/commands/score.command.js +261 -0
  33. package/dist/cli/commands/score.command.js.map +1 -0
  34. package/dist/cli/utils/index.d.ts +10 -0
  35. package/dist/cli/utils/index.d.ts.map +1 -0
  36. package/dist/cli/utils/index.js +10 -0
  37. package/dist/cli/utils/index.js.map +1 -0
  38. package/dist/cli/utils/output.d.ts +192 -0
  39. package/dist/cli/utils/output.d.ts.map +1 -0
  40. package/dist/cli/utils/output.js +411 -0
  41. package/dist/cli/utils/output.js.map +1 -0
  42. package/dist/cli/utils/progress.d.ts +204 -0
  43. package/dist/cli/utils/progress.d.ts.map +1 -0
  44. package/dist/cli/utils/progress.js +396 -0
  45. package/dist/cli/utils/progress.js.map +1 -0
  46. package/dist/core/types/index.d.ts +1 -0
  47. package/dist/core/types/index.d.ts.map +1 -1
  48. package/dist/core/types/project.types.d.ts +3 -3
  49. package/dist/core/types/project.types.d.ts.map +1 -1
  50. package/dist/core/types/scoring.types.d.ts +301 -0
  51. package/dist/core/types/scoring.types.d.ts.map +1 -0
  52. package/dist/core/types/scoring.types.js +8 -0
  53. package/dist/core/types/scoring.types.js.map +1 -0
  54. package/dist/services/detection/framework-detector.d.ts.map +1 -1
  55. package/dist/services/detection/framework-detector.js +74 -5
  56. package/dist/services/detection/framework-detector.js.map +1 -1
  57. package/dist/services/index.d.ts +12 -0
  58. package/dist/services/index.d.ts.map +1 -1
  59. package/dist/services/index.js +14 -0
  60. package/dist/services/index.js.map +1 -1
  61. package/dist/services/optimization/detectors/bug-detector.d.ts +82 -0
  62. package/dist/services/optimization/detectors/bug-detector.d.ts.map +1 -0
  63. package/dist/services/optimization/detectors/bug-detector.js +443 -0
  64. package/dist/services/optimization/detectors/bug-detector.js.map +1 -0
  65. package/dist/services/optimization/detectors/code-smell-detector.d.ts +108 -0
  66. package/dist/services/optimization/detectors/code-smell-detector.d.ts.map +1 -0
  67. package/dist/services/optimization/detectors/code-smell-detector.js +569 -0
  68. package/dist/services/optimization/detectors/code-smell-detector.js.map +1 -0
  69. package/dist/services/optimization/detectors/index.d.ts +7 -0
  70. package/dist/services/optimization/detectors/index.d.ts.map +1 -0
  71. package/dist/services/optimization/detectors/index.js +7 -0
  72. package/dist/services/optimization/detectors/index.js.map +1 -0
  73. package/dist/services/optimization/detectors/perf-detector.d.ts +80 -0
  74. package/dist/services/optimization/detectors/perf-detector.d.ts.map +1 -0
  75. package/dist/services/optimization/detectors/perf-detector.js +451 -0
  76. package/dist/services/optimization/detectors/perf-detector.js.map +1 -0
  77. package/dist/services/optimization/index.d.ts +61 -0
  78. package/dist/services/optimization/index.d.ts.map +1 -0
  79. package/dist/services/optimization/index.js +69 -0
  80. package/dist/services/optimization/index.js.map +1 -0
  81. package/dist/services/optimization/optimization.service.d.ts +65 -0
  82. package/dist/services/optimization/optimization.service.d.ts.map +1 -0
  83. package/dist/services/optimization/optimization.service.js +511 -0
  84. package/dist/services/optimization/optimization.service.js.map +1 -0
  85. package/dist/services/optimization/optimization.types.d.ts +343 -0
  86. package/dist/services/optimization/optimization.types.d.ts.map +1 -0
  87. package/dist/services/optimization/optimization.types.js +8 -0
  88. package/dist/services/optimization/optimization.types.js.map +1 -0
  89. package/dist/services/optimization/optimizers/code-optimizer.d.ts +87 -0
  90. package/dist/services/optimization/optimizers/code-optimizer.d.ts.map +1 -0
  91. package/dist/services/optimization/optimizers/code-optimizer.js +436 -0
  92. package/dist/services/optimization/optimizers/code-optimizer.js.map +1 -0
  93. package/dist/services/optimization/optimizers/index.d.ts +7 -0
  94. package/dist/services/optimization/optimizers/index.d.ts.map +1 -0
  95. package/dist/services/optimization/optimizers/index.js +7 -0
  96. package/dist/services/optimization/optimizers/index.js.map +1 -0
  97. package/dist/services/optimization/optimizers/perf-optimizer.d.ts +64 -0
  98. package/dist/services/optimization/optimizers/perf-optimizer.d.ts.map +1 -0
  99. package/dist/services/optimization/optimizers/perf-optimizer.js +330 -0
  100. package/dist/services/optimization/optimizers/perf-optimizer.js.map +1 -0
  101. package/dist/services/optimization/optimizers/refact-optimizer.d.ts +82 -0
  102. package/dist/services/optimization/optimizers/refact-optimizer.d.ts.map +1 -0
  103. package/dist/services/optimization/optimizers/refact-optimizer.js +354 -0
  104. package/dist/services/optimization/optimizers/refact-optimizer.js.map +1 -0
  105. package/dist/services/optimization/patterns/anti-patterns.d.ts +31 -0
  106. package/dist/services/optimization/patterns/anti-patterns.d.ts.map +1 -0
  107. package/dist/services/optimization/patterns/anti-patterns.js +501 -0
  108. package/dist/services/optimization/patterns/anti-patterns.js.map +1 -0
  109. package/dist/services/optimization/patterns/index.d.ts +5 -0
  110. package/dist/services/optimization/patterns/index.d.ts.map +1 -0
  111. package/dist/services/optimization/patterns/index.js +5 -0
  112. package/dist/services/optimization/patterns/index.js.map +1 -0
  113. package/dist/services/reporting/export/chart.exporter.d.ts +59 -0
  114. package/dist/services/reporting/export/chart.exporter.d.ts.map +1 -0
  115. package/dist/services/reporting/export/chart.exporter.js +350 -0
  116. package/dist/services/reporting/export/chart.exporter.js.map +1 -0
  117. package/dist/services/reporting/export/index.d.ts +9 -0
  118. package/dist/services/reporting/export/index.d.ts.map +1 -0
  119. package/dist/services/reporting/export/index.js +10 -0
  120. package/dist/services/reporting/export/index.js.map +1 -0
  121. package/dist/services/reporting/export/pdf.exporter.d.ts +133 -0
  122. package/dist/services/reporting/export/pdf.exporter.d.ts.map +1 -0
  123. package/dist/services/reporting/export/pdf.exporter.js +270 -0
  124. package/dist/services/reporting/export/pdf.exporter.js.map +1 -0
  125. package/dist/services/reporting/history.service.d.ts +93 -0
  126. package/dist/services/reporting/history.service.d.ts.map +1 -0
  127. package/dist/services/reporting/history.service.js +285 -0
  128. package/dist/services/reporting/history.service.js.map +1 -0
  129. package/dist/services/reporting/index.d.ts +15 -0
  130. package/dist/services/reporting/index.d.ts.map +1 -0
  131. package/dist/services/reporting/index.js +16 -0
  132. package/dist/services/reporting/index.js.map +1 -0
  133. package/dist/services/reporting/report.service.d.ts +102 -0
  134. package/dist/services/reporting/report.service.d.ts.map +1 -0
  135. package/dist/services/reporting/report.service.js +240 -0
  136. package/dist/services/reporting/report.service.js.map +1 -0
  137. package/dist/services/reporting/reporting.types.d.ts +329 -0
  138. package/dist/services/reporting/reporting.types.d.ts.map +1 -0
  139. package/dist/services/reporting/reporting.types.js +8 -0
  140. package/dist/services/reporting/reporting.types.js.map +1 -0
  141. package/dist/services/reporting/templates/html.template.d.ts +81 -0
  142. package/dist/services/reporting/templates/html.template.d.ts.map +1 -0
  143. package/dist/services/reporting/templates/html.template.js +741 -0
  144. package/dist/services/reporting/templates/html.template.js.map +1 -0
  145. package/dist/services/reporting/templates/json.template.d.ts +85 -0
  146. package/dist/services/reporting/templates/json.template.d.ts.map +1 -0
  147. package/dist/services/reporting/templates/json.template.js +308 -0
  148. package/dist/services/reporting/templates/json.template.js.map +1 -0
  149. package/dist/services/reporting/templates/markdown.template.d.ts +69 -0
  150. package/dist/services/reporting/templates/markdown.template.d.ts.map +1 -0
  151. package/dist/services/reporting/templates/markdown.template.js +311 -0
  152. package/dist/services/reporting/templates/markdown.template.js.map +1 -0
  153. package/dist/services/reporting/trend-analyzer.d.ts +73 -0
  154. package/dist/services/reporting/trend-analyzer.d.ts.map +1 -0
  155. package/dist/services/reporting/trend-analyzer.js +291 -0
  156. package/dist/services/reporting/trend-analyzer.js.map +1 -0
  157. package/dist/services/review/analyzers/dependency-analyzer.d.ts +87 -0
  158. package/dist/services/review/analyzers/dependency-analyzer.d.ts.map +1 -0
  159. package/dist/services/review/analyzers/dependency-analyzer.js +458 -0
  160. package/dist/services/review/analyzers/dependency-analyzer.js.map +1 -0
  161. package/dist/services/review/analyzers/index.d.ts +13 -0
  162. package/dist/services/review/analyzers/index.d.ts.map +1 -0
  163. package/dist/services/review/analyzers/index.js +13 -0
  164. package/dist/services/review/analyzers/index.js.map +1 -0
  165. package/dist/services/review/analyzers/nestjs-analyzer.d.ts +210 -0
  166. package/dist/services/review/analyzers/nestjs-analyzer.d.ts.map +1 -0
  167. package/dist/services/review/analyzers/nestjs-analyzer.js +571 -0
  168. package/dist/services/review/analyzers/nestjs-analyzer.js.map +1 -0
  169. package/dist/services/review/analyzers/performance-analyzer.d.ts +91 -0
  170. package/dist/services/review/analyzers/performance-analyzer.d.ts.map +1 -0
  171. package/dist/services/review/analyzers/performance-analyzer.js +589 -0
  172. package/dist/services/review/analyzers/performance-analyzer.js.map +1 -0
  173. package/dist/services/review/analyzers/security-analyzer.d.ts +96 -0
  174. package/dist/services/review/analyzers/security-analyzer.d.ts.map +1 -0
  175. package/dist/services/review/analyzers/security-analyzer.js +512 -0
  176. package/dist/services/review/analyzers/security-analyzer.js.map +1 -0
  177. package/dist/services/review/analyzers/static-analyzer.d.ts +90 -0
  178. package/dist/services/review/analyzers/static-analyzer.d.ts.map +1 -0
  179. package/dist/services/review/analyzers/static-analyzer.js +423 -0
  180. package/dist/services/review/analyzers/static-analyzer.js.map +1 -0
  181. package/dist/services/review/fixers/auto-fixer.d.ts +94 -0
  182. package/dist/services/review/fixers/auto-fixer.d.ts.map +1 -0
  183. package/dist/services/review/fixers/auto-fixer.js +404 -0
  184. package/dist/services/review/fixers/auto-fixer.js.map +1 -0
  185. package/dist/services/review/fixers/index.d.ts +11 -0
  186. package/dist/services/review/fixers/index.d.ts.map +1 -0
  187. package/dist/services/review/fixers/index.js +11 -0
  188. package/dist/services/review/fixers/index.js.map +1 -0
  189. package/dist/services/review/fixers/refactor-suggester.d.ts +100 -0
  190. package/dist/services/review/fixers/refactor-suggester.d.ts.map +1 -0
  191. package/dist/services/review/fixers/refactor-suggester.js +555 -0
  192. package/dist/services/review/fixers/refactor-suggester.js.map +1 -0
  193. package/dist/services/review/fixers/test-generator.d.ts +99 -0
  194. package/dist/services/review/fixers/test-generator.d.ts.map +1 -0
  195. package/dist/services/review/fixers/test-generator.js +458 -0
  196. package/dist/services/review/fixers/test-generator.js.map +1 -0
  197. package/dist/services/review/index.d.ts +14 -0
  198. package/dist/services/review/index.d.ts.map +1 -0
  199. package/dist/services/review/index.js +14 -0
  200. package/dist/services/review/index.js.map +1 -0
  201. package/dist/services/review/reporters/fix-reporter.d.ts +67 -0
  202. package/dist/services/review/reporters/fix-reporter.d.ts.map +1 -0
  203. package/dist/services/review/reporters/fix-reporter.js +437 -0
  204. package/dist/services/review/reporters/fix-reporter.js.map +1 -0
  205. package/dist/services/review/reporters/index.d.ts +10 -0
  206. package/dist/services/review/reporters/index.d.ts.map +1 -0
  207. package/dist/services/review/reporters/index.js +10 -0
  208. package/dist/services/review/reporters/index.js.map +1 -0
  209. package/dist/services/review/reporters/score-reporter.d.ts +84 -0
  210. package/dist/services/review/reporters/score-reporter.d.ts.map +1 -0
  211. package/dist/services/review/reporters/score-reporter.js +560 -0
  212. package/dist/services/review/reporters/score-reporter.js.map +1 -0
  213. package/dist/services/review/review.service.d.ts +129 -0
  214. package/dist/services/review/review.service.d.ts.map +1 -0
  215. package/dist/services/review/review.service.js +396 -0
  216. package/dist/services/review/review.service.js.map +1 -0
  217. package/dist/services/review/review.types.d.ts +443 -0
  218. package/dist/services/review/review.types.d.ts.map +1 -0
  219. package/dist/services/review/review.types.js +11 -0
  220. package/dist/services/review/review.types.js.map +1 -0
  221. package/dist/services/scoring/dimensions/accessibility.analyzer.d.ts +53 -0
  222. package/dist/services/scoring/dimensions/accessibility.analyzer.d.ts.map +1 -0
  223. package/dist/services/scoring/dimensions/accessibility.analyzer.js +260 -0
  224. package/dist/services/scoring/dimensions/accessibility.analyzer.js.map +1 -0
  225. package/dist/services/scoring/dimensions/backend-logic.analyzer.d.ts +138 -0
  226. package/dist/services/scoring/dimensions/backend-logic.analyzer.d.ts.map +1 -0
  227. package/dist/services/scoring/dimensions/backend-logic.analyzer.js +713 -0
  228. package/dist/services/scoring/dimensions/backend-logic.analyzer.js.map +1 -0
  229. package/dist/services/scoring/dimensions/business-logic.analyzer.d.ts +142 -0
  230. package/dist/services/scoring/dimensions/business-logic.analyzer.d.ts.map +1 -0
  231. package/dist/services/scoring/dimensions/business-logic.analyzer.js +747 -0
  232. package/dist/services/scoring/dimensions/business-logic.analyzer.js.map +1 -0
  233. package/dist/services/scoring/dimensions/code-quality.analyzer.d.ts +142 -0
  234. package/dist/services/scoring/dimensions/code-quality.analyzer.d.ts.map +1 -0
  235. package/dist/services/scoring/dimensions/code-quality.analyzer.js +685 -0
  236. package/dist/services/scoring/dimensions/code-quality.analyzer.js.map +1 -0
  237. package/dist/services/scoring/dimensions/index.d.ts +18 -0
  238. package/dist/services/scoring/dimensions/index.d.ts.map +1 -0
  239. package/dist/services/scoring/dimensions/index.js +27 -0
  240. package/dist/services/scoring/dimensions/index.js.map +1 -0
  241. package/dist/services/scoring/dimensions/performance.analyzer.d.ts +125 -0
  242. package/dist/services/scoring/dimensions/performance.analyzer.d.ts.map +1 -0
  243. package/dist/services/scoring/dimensions/performance.analyzer.js +615 -0
  244. package/dist/services/scoring/dimensions/performance.analyzer.js.map +1 -0
  245. package/dist/services/scoring/dimensions/security.analyzer.d.ts +53 -0
  246. package/dist/services/scoring/dimensions/security.analyzer.d.ts.map +1 -0
  247. package/dist/services/scoring/dimensions/security.analyzer.js +327 -0
  248. package/dist/services/scoring/dimensions/security.analyzer.js.map +1 -0
  249. package/dist/services/scoring/dimensions/seo.analyzer.d.ts +77 -0
  250. package/dist/services/scoring/dimensions/seo.analyzer.d.ts.map +1 -0
  251. package/dist/services/scoring/dimensions/seo.analyzer.js +502 -0
  252. package/dist/services/scoring/dimensions/seo.analyzer.js.map +1 -0
  253. package/dist/services/scoring/dimensions/test-coverage.analyzer.d.ts +106 -0
  254. package/dist/services/scoring/dimensions/test-coverage.analyzer.d.ts.map +1 -0
  255. package/dist/services/scoring/dimensions/test-coverage.analyzer.js +496 -0
  256. package/dist/services/scoring/dimensions/test-coverage.analyzer.js.map +1 -0
  257. package/dist/services/scoring/dimensions/ui-ux.analyzer.d.ts +126 -0
  258. package/dist/services/scoring/dimensions/ui-ux.analyzer.d.ts.map +1 -0
  259. package/dist/services/scoring/dimensions/ui-ux.analyzer.js +665 -0
  260. package/dist/services/scoring/dimensions/ui-ux.analyzer.js.map +1 -0
  261. package/dist/services/scoring/index.d.ts +10 -0
  262. package/dist/services/scoring/index.d.ts.map +1 -0
  263. package/dist/services/scoring/index.js +10 -0
  264. package/dist/services/scoring/index.js.map +1 -0
  265. package/dist/services/scoring/scoring-service.d.ts +222 -0
  266. package/dist/services/scoring/scoring-service.d.ts.map +1 -0
  267. package/dist/services/scoring/scoring-service.js +636 -0
  268. package/dist/services/scoring/scoring-service.js.map +1 -0
  269. package/package.json +11 -3
  270. package/templates/README.md +183 -0
  271. package/templates/nestjs/controller.spec.ts +203 -0
  272. package/templates/nestjs/e2e/api.e2e-spec.ts +451 -0
  273. package/templates/nestjs/e2e/auth.e2e-spec.ts +533 -0
  274. package/templates/nestjs/fixtures/test-module.ts +311 -0
  275. package/templates/nestjs/guard.spec.ts +314 -0
  276. package/templates/nestjs/interceptor.spec.ts +458 -0
  277. package/templates/nestjs/module.spec.ts +173 -0
  278. package/templates/nestjs/pipe.spec.ts +474 -0
  279. package/templates/nestjs/service.spec.ts +296 -0
  280. package/templates/rust/Cargo.toml +72 -0
  281. package/templates/rust/actix-controller.test.rs +114 -0
  282. package/templates/rust/axum-handler.test.rs +117 -0
  283. package/templates/rust/integration.test.rs +63 -0
  284. package/templates/rust/rocket-route.test.rs +106 -0
  285. package/templates/rust/unit.test.rs +38 -0
@@ -0,0 +1,665 @@
1
+ /**
2
+ * UI/UX Analyzer
3
+ *
4
+ * Analyzes UI/UX quality including responsive design, loading states,
5
+ * error handling UX, form validation feedback, and empty states.
6
+ *
7
+ * @module services/scoring/dimensions/ui-ux
8
+ */
9
+ import { readFile, readdir } from 'node:fs/promises';
10
+ import { join, extname } 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
+ * UI/UX Analyzer
16
+ *
17
+ * Evaluates UI/UX quality across multiple dimensions:
18
+ * - Responsive design (viewport meta, media queries)
19
+ * - Loading states (skeletons, spinners)
20
+ * - Error handling UX
21
+ * - Form validation feedback
22
+ * - Empty states
23
+ */
24
+ export class UiUxAnalyzer {
25
+ /** Analyzer configuration */
26
+ config = {
27
+ dimension: 'ui-ux',
28
+ defaultWeight: 0.10,
29
+ estimatedDuration: 20000,
30
+ supportedFrameworks: ['Next.js', 'React', 'Vue', 'Nuxt', 'Svelte', 'Angular', 'Remix', 'SvelteKit', 'Astro', 'Gatsby'],
31
+ };
32
+ logger;
33
+ executor;
34
+ _checkResponsive;
35
+ _checkLoadingStates;
36
+ _checkErrorHandling;
37
+ _checkFormValidation;
38
+ _checkEmptyStates;
39
+ constructor(options = {}) {
40
+ this.logger = createLogger('UiUxAnalyzer');
41
+ this.executor = new CommandExecutor();
42
+ this._checkResponsive = options.checkResponsive ?? true;
43
+ this._checkLoadingStates = options.checkLoadingStates ?? true;
44
+ this._checkErrorHandling = options.checkErrorHandling ?? true;
45
+ this._checkFormValidation = options.checkFormValidation ?? true;
46
+ this._checkEmptyStates = options.checkEmptyStates ?? true;
47
+ }
48
+ /**
49
+ * Get the dimension this analyzer handles
50
+ */
51
+ getDimension() {
52
+ return 'ui-ux';
53
+ }
54
+ /**
55
+ * Get the default weight for this dimension
56
+ */
57
+ getWeight() {
58
+ return 0.10; // 10% weight in overall score
59
+ }
60
+ /**
61
+ * Analyze UI/UX 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 UI/UX metrics
67
+ */
68
+ async analyze(projectPath, _framework, _options) {
69
+ const startTime = performance.now();
70
+ this.logger.info(`Analyzing UI/UX for: ${projectPath}`);
71
+ const issues = [];
72
+ const improvements = [];
73
+ try {
74
+ // Check responsive design
75
+ const responsiveMetrics = this._checkResponsive ? await this.checkResponsiveDesign(projectPath) : { hasResponsiveDesign: false, breakpointCount: 0 };
76
+ // Check loading states
77
+ const loadingMetrics = this._checkLoadingStates ? await this.checkLoadingStatesPatterns(projectPath) : { hasLoadingStates: false, loadingPatterns: new Set() };
78
+ // Check error handling
79
+ const errorMetrics = this._checkErrorHandling ? await this.checkErrorHandlingPatterns(projectPath) : { hasErrorHandling: false, errorPatterns: new Set() };
80
+ // Check form validation
81
+ const validationMetrics = this._checkFormValidation ? await this.checkFormValidationPatterns(projectPath) : { hasFormValidation: false, validationPatterns: new Set() };
82
+ // Check empty states
83
+ const emptyStateMetrics = this._checkEmptyStates ? await this.checkEmptyStatesPatterns(projectPath) : { hasEmptyStates: false, emptyStateCount: 0 };
84
+ const metrics = {
85
+ ...responsiveMetrics,
86
+ ...loadingMetrics,
87
+ ...errorMetrics,
88
+ ...validationMetrics,
89
+ ...emptyStateMetrics,
90
+ };
91
+ // Build issues list
92
+ issues.push(...this.identifyUiUxIssues(metrics));
93
+ // Build improvements list
94
+ improvements.push(...this.generateUiUxImprovements(metrics));
95
+ // Calculate final score
96
+ const score = this.calculateUiUxScore(metrics);
97
+ const duration = Math.round(performance.now() - startTime);
98
+ return {
99
+ dimension: this.getDimension(),
100
+ score,
101
+ weight: this.getWeight(),
102
+ weightedScore: score * this.getWeight(),
103
+ issues,
104
+ improvements,
105
+ metadata: {
106
+ itemsChecked: 5,
107
+ itemsPassed: this.countPassedChecks(metrics),
108
+ metrics: {
109
+ hasResponsiveDesign: metrics.hasResponsiveDesign ? 1 : 0,
110
+ breakpointCount: metrics.breakpointCount,
111
+ hasLoadingStates: metrics.hasLoadingStates ? 1 : 0,
112
+ loadingPatternCount: metrics.loadingPatterns.size,
113
+ hasErrorHandling: metrics.hasErrorHandling ? 1 : 0,
114
+ errorPatternCount: metrics.errorPatterns.size,
115
+ hasFormValidation: metrics.hasFormValidation ? 1 : 0,
116
+ validationPatternCount: metrics.validationPatterns.size,
117
+ hasEmptyStates: metrics.hasEmptyStates ? 1 : 0,
118
+ emptyStateCount: metrics.emptyStateCount,
119
+ },
120
+ },
121
+ };
122
+ }
123
+ catch (error) {
124
+ this.logger.error('Error analyzing UI/UX', error);
125
+ issues.push({
126
+ severity: 'medium',
127
+ category: 'ux',
128
+ description: `Failed to analyze UI/UX: ${error instanceof Error ? error.message : String(error)}`,
129
+ fixable: false,
130
+ });
131
+ const duration = Math.round(performance.now() - startTime);
132
+ return {
133
+ dimension: this.getDimension(),
134
+ score: 50,
135
+ weight: this.getWeight(),
136
+ weightedScore: 50 * this.getWeight(),
137
+ issues,
138
+ improvements,
139
+ metadata: { error: String(error) },
140
+ };
141
+ }
142
+ }
143
+ /**
144
+ * Check for responsive design
145
+ */
146
+ async checkResponsiveDesign(projectPath) {
147
+ let hasResponsiveDesign = false;
148
+ let breakpointCount = 0;
149
+ // Check for viewport meta tag
150
+ const indexHtml = join(projectPath, 'index.html');
151
+ if (existsSync(indexHtml)) {
152
+ try {
153
+ const content = await readFile(indexHtml, 'utf-8');
154
+ if (content.includes('viewport')) {
155
+ hasResponsiveDesign = true;
156
+ }
157
+ }
158
+ catch {
159
+ // Ignore
160
+ }
161
+ }
162
+ // Check for media queries in CSS/SCSS files
163
+ const srcDir = join(projectPath, 'src');
164
+ const appDir = join(projectPath, 'app');
165
+ const dirsToCheck = [];
166
+ if (existsSync(srcDir))
167
+ dirsToCheck.push(srcDir);
168
+ if (existsSync(appDir))
169
+ dirsToCheck.push(appDir);
170
+ for (const dir of dirsToCheck) {
171
+ await this.scanDirectoryForMediaQueries(dir, (count) => {
172
+ if (count > 0) {
173
+ hasResponsiveDesign = true;
174
+ breakpointCount += count;
175
+ }
176
+ });
177
+ }
178
+ // Check Tailwind or other responsive utilities
179
+ try {
180
+ const pkgPath = join(projectPath, 'package.json');
181
+ const pkgContent = await readFile(pkgPath, 'utf-8');
182
+ const pkg = JSON.parse(pkgContent);
183
+ if (pkg.devDependencies?.tailwindcss || pkg.dependencies?.tailwindcss) {
184
+ hasResponsiveDesign = true;
185
+ // Tailwind has default breakpoints
186
+ breakpointCount = Math.max(breakpointCount, 5); // sm, md, lg, xl, 2xl
187
+ }
188
+ }
189
+ catch {
190
+ // Ignore
191
+ }
192
+ return { hasResponsiveDesign, breakpointCount };
193
+ }
194
+ /**
195
+ * Scan directory for media queries
196
+ */
197
+ async scanDirectoryForMediaQueries(dir, callback) {
198
+ try {
199
+ const entries = await readdir(dir, { withFileTypes: true });
200
+ for (const entry of entries) {
201
+ const fullPath = join(dir, entry.name);
202
+ if (entry.isDirectory()) {
203
+ if (!['node_modules', '.git', 'dist', 'build', 'coverage'].includes(entry.name)) {
204
+ await this.scanDirectoryForMediaQueries(fullPath, callback);
205
+ }
206
+ }
207
+ else if (entry.isFile()) {
208
+ const ext = extname(entry.name);
209
+ if (['.css', '.scss', '.sass', '.less'].includes(ext)) {
210
+ try {
211
+ const content = await readFile(fullPath, 'utf-8');
212
+ const mediaQueryCount = (content.match(/@media/g) ?? []).length;
213
+ callback(mediaQueryCount);
214
+ }
215
+ catch {
216
+ // Skip unreadable files
217
+ }
218
+ }
219
+ }
220
+ }
221
+ }
222
+ catch {
223
+ // Directory not accessible
224
+ }
225
+ }
226
+ /**
227
+ * Check for loading states
228
+ */
229
+ async checkLoadingStatesPatterns(projectPath) {
230
+ const loadingPatterns = new Set();
231
+ const srcDir = join(projectPath, 'src');
232
+ const appDir = join(projectPath, 'app');
233
+ const dirsToCheck = [];
234
+ if (existsSync(srcDir))
235
+ dirsToCheck.push(srcDir);
236
+ if (existsSync(appDir))
237
+ dirsToCheck.push(appDir);
238
+ // Loading patterns to detect
239
+ const patterns = new Map([
240
+ ['skeleton', [/skeleton/i, /Skeleton/i]],
241
+ ['spinner', [/spinner/i, /Spinner/i, /loading/i, /Loading/i]],
242
+ ['progress', [/progress/i, /Progress/i]],
243
+ ['inline', [/isLoading/i, /isPending/i, /pending/i]],
244
+ ]);
245
+ for (const dir of dirsToCheck) {
246
+ await this.scanDirectoryForLoadingPatterns(dir, patterns, loadingPatterns);
247
+ }
248
+ return {
249
+ hasLoadingStates: loadingPatterns.size > 0,
250
+ loadingPatterns,
251
+ };
252
+ }
253
+ /**
254
+ * Scan directory for loading patterns
255
+ */
256
+ async scanDirectoryForLoadingPatterns(dir, patterns, foundPatterns) {
257
+ try {
258
+ const entries = await readdir(dir, { withFileTypes: true });
259
+ for (const entry of entries) {
260
+ const fullPath = join(dir, entry.name);
261
+ if (entry.isDirectory()) {
262
+ if (!['node_modules', '.git', 'dist', 'build', 'coverage'].includes(entry.name)) {
263
+ await this.scanDirectoryForLoadingPatterns(fullPath, patterns, foundPatterns);
264
+ }
265
+ }
266
+ else if (entry.isFile()) {
267
+ const ext = extname(entry.name);
268
+ if (['.tsx', '.jsx', '.ts', '.js'].includes(ext)) {
269
+ try {
270
+ const content = await readFile(fullPath, 'utf-8');
271
+ for (const [patternType, regexList] of Array.from(patterns.entries())) {
272
+ for (const regex of regexList) {
273
+ if (regex.test(content)) {
274
+ foundPatterns.add(patternType);
275
+ break;
276
+ }
277
+ }
278
+ }
279
+ }
280
+ catch {
281
+ // Skip unreadable files
282
+ }
283
+ }
284
+ }
285
+ }
286
+ }
287
+ catch {
288
+ // Directory not accessible
289
+ }
290
+ }
291
+ /**
292
+ * Check for error handling
293
+ */
294
+ async checkErrorHandlingPatterns(projectPath) {
295
+ const errorPatterns = new Set();
296
+ const srcDir = join(projectPath, 'src');
297
+ const appDir = join(projectPath, 'app');
298
+ const dirsToCheck = [];
299
+ if (existsSync(srcDir))
300
+ dirsToCheck.push(srcDir);
301
+ if (existsSync(appDir))
302
+ dirsToCheck.push(appDir);
303
+ // Error patterns to detect
304
+ const patterns = new Map([
305
+ ['boundary', [/ErrorBoundary/i, /error-boundary/i]],
306
+ ['toast', [/toast/i, /Toast/i, /notification/i]],
307
+ ['inline', [/error.*message/i, /showError/i]],
308
+ ['page', [/\b404\b/, /\b500\b/, /notfound/i, /error.*page/i]],
309
+ ]);
310
+ for (const dir of dirsToCheck) {
311
+ await this.scanDirectoryForErrorPatterns(dir, patterns, errorPatterns);
312
+ }
313
+ return {
314
+ hasErrorHandling: errorPatterns.size > 0,
315
+ errorPatterns,
316
+ };
317
+ }
318
+ /**
319
+ * Scan directory for error patterns
320
+ */
321
+ async scanDirectoryForErrorPatterns(dir, patterns, foundPatterns) {
322
+ try {
323
+ const entries = await readdir(dir, { withFileTypes: true });
324
+ for (const entry of entries) {
325
+ const fullPath = join(dir, entry.name);
326
+ if (entry.isDirectory()) {
327
+ if (!['node_modules', '.git', 'dist', 'build', 'coverage'].includes(entry.name)) {
328
+ await this.scanDirectoryForErrorPatterns(fullPath, patterns, foundPatterns);
329
+ }
330
+ }
331
+ else if (entry.isFile()) {
332
+ const ext = extname(entry.name);
333
+ if (['.tsx', '.jsx', '.ts', '.js'].includes(ext)) {
334
+ try {
335
+ const content = await readFile(fullPath, 'utf-8');
336
+ for (const [patternType, regexList] of Array.from(patterns.entries())) {
337
+ for (const regex of regexList) {
338
+ if (regex.test(content)) {
339
+ foundPatterns.add(patternType);
340
+ break;
341
+ }
342
+ }
343
+ }
344
+ }
345
+ catch {
346
+ // Skip unreadable files
347
+ }
348
+ }
349
+ }
350
+ }
351
+ }
352
+ catch {
353
+ // Directory not accessible
354
+ }
355
+ }
356
+ /**
357
+ * Check for form validation
358
+ */
359
+ async checkFormValidationPatterns(projectPath) {
360
+ const validationPatterns = new Set();
361
+ const srcDir = join(projectPath, 'src');
362
+ const appDir = join(projectPath, 'app');
363
+ const dirsToCheck = [];
364
+ if (existsSync(srcDir))
365
+ dirsToCheck.push(srcDir);
366
+ if (existsSync(appDir))
367
+ dirsToCheck.push(appDir);
368
+ // Validation patterns to detect
369
+ const patterns = new Map([
370
+ ['inline', [/error.*text/i, /validation.*message/i, /helperText/i]],
371
+ ['onblur', [/onBlur/i, /blur.*validation/i]],
372
+ ['onsubmit', [/onSubmit.*validation/i, /validate.*form/i]],
373
+ ['realtime', [/onChange.*validation/i, /watch.*form/i]],
374
+ ]);
375
+ for (const dir of dirsToCheck) {
376
+ await this.scanDirectoryForValidationPatterns(dir, patterns, validationPatterns);
377
+ }
378
+ // Check for React Hook Form or Formik
379
+ try {
380
+ const pkgPath = join(projectPath, 'package.json');
381
+ const pkgContent = await readFile(pkgPath, 'utf-8');
382
+ const pkg = JSON.parse(pkgContent);
383
+ if (pkg.dependencies?.['react-hook-form'] || pkg.dependencies?.formik) {
384
+ validationPatterns.add('realtime');
385
+ }
386
+ }
387
+ catch {
388
+ // Ignore
389
+ }
390
+ return {
391
+ hasFormValidation: validationPatterns.size > 0,
392
+ validationPatterns,
393
+ };
394
+ }
395
+ /**
396
+ * Scan directory for validation patterns
397
+ */
398
+ async scanDirectoryForValidationPatterns(dir, patterns, foundPatterns) {
399
+ try {
400
+ const entries = await readdir(dir, { withFileTypes: true });
401
+ for (const entry of entries) {
402
+ const fullPath = join(dir, entry.name);
403
+ if (entry.isDirectory()) {
404
+ if (!['node_modules', '.git', 'dist', 'build', 'coverage'].includes(entry.name)) {
405
+ await this.scanDirectoryForValidationPatterns(fullPath, patterns, foundPatterns);
406
+ }
407
+ }
408
+ else if (entry.isFile()) {
409
+ const ext = extname(entry.name);
410
+ if (['.tsx', '.jsx', '.ts', '.js'].includes(ext)) {
411
+ try {
412
+ const content = await readFile(fullPath, 'utf-8');
413
+ for (const [patternType, regexList] of Array.from(patterns.entries())) {
414
+ for (const regex of regexList) {
415
+ if (regex.test(content)) {
416
+ foundPatterns.add(patternType);
417
+ break;
418
+ }
419
+ }
420
+ }
421
+ }
422
+ catch {
423
+ // Skip unreadable files
424
+ }
425
+ }
426
+ }
427
+ }
428
+ }
429
+ catch {
430
+ // Directory not accessible
431
+ }
432
+ }
433
+ /**
434
+ * Check for empty states
435
+ */
436
+ async checkEmptyStatesPatterns(projectPath) {
437
+ let emptyStateCount = 0;
438
+ let hasEmptyStates = false;
439
+ const srcDir = join(projectPath, 'src');
440
+ const appDir = join(projectPath, 'app');
441
+ const dirsToCheck = [];
442
+ if (existsSync(srcDir))
443
+ dirsToCheck.push(srcDir);
444
+ if (existsSync(appDir))
445
+ dirsToCheck.push(appDir);
446
+ // Empty state patterns
447
+ const patterns = [
448
+ /empty/i,
449
+ /no.*data/i,
450
+ /no.*results/i,
451
+ /nothing.*found/i,
452
+ /zero.*state/i,
453
+ ];
454
+ for (const dir of dirsToCheck) {
455
+ await this.scanDirectoryForEmptyStates(dir, patterns, (count) => {
456
+ if (count > 0) {
457
+ hasEmptyStates = true;
458
+ emptyStateCount += count;
459
+ }
460
+ });
461
+ }
462
+ return { hasEmptyStates, emptyStateCount };
463
+ }
464
+ /**
465
+ * Scan directory for empty states
466
+ */
467
+ async scanDirectoryForEmptyStates(dir, patterns, callback) {
468
+ try {
469
+ const entries = await readdir(dir, { withFileTypes: true });
470
+ for (const entry of entries) {
471
+ const fullPath = join(dir, entry.name);
472
+ if (entry.isDirectory()) {
473
+ if (!['node_modules', '.git', 'dist', 'build', 'coverage'].includes(entry.name)) {
474
+ await this.scanDirectoryForEmptyStates(fullPath, patterns, callback);
475
+ }
476
+ }
477
+ else if (entry.isFile()) {
478
+ const ext = extname(entry.name);
479
+ if (['.tsx', '.jsx', '.ts', '.js'].includes(ext)) {
480
+ try {
481
+ const content = await readFile(fullPath, 'utf-8');
482
+ let count = 0;
483
+ for (const pattern of patterns) {
484
+ count += (content.match(pattern) ?? []).length;
485
+ }
486
+ callback(count);
487
+ }
488
+ catch {
489
+ // Skip unreadable files
490
+ }
491
+ }
492
+ }
493
+ }
494
+ }
495
+ catch {
496
+ // Directory not accessible
497
+ }
498
+ }
499
+ /**
500
+ * Identify UI/UX issues
501
+ */
502
+ identifyUiUxIssues(metrics) {
503
+ const issues = [];
504
+ if (!metrics.hasResponsiveDesign) {
505
+ issues.push({
506
+ severity: 'high',
507
+ category: 'ux',
508
+ description: 'No responsive design detected',
509
+ fixable: true,
510
+ suggestion: 'Add responsive design with media queries or a responsive framework',
511
+ });
512
+ }
513
+ if (!metrics.hasLoadingStates) {
514
+ issues.push({
515
+ severity: 'medium',
516
+ category: 'ux',
517
+ description: 'No loading states detected',
518
+ fixable: true,
519
+ suggestion: 'Add loading indicators (skeletons, spinners) for async operations',
520
+ });
521
+ }
522
+ if (!metrics.hasErrorHandling) {
523
+ issues.push({
524
+ severity: 'high',
525
+ category: 'ux',
526
+ description: 'No error handling UX detected',
527
+ fixable: true,
528
+ suggestion: 'Implement error boundaries and user-friendly error messages',
529
+ });
530
+ }
531
+ if (!metrics.hasFormValidation) {
532
+ issues.push({
533
+ severity: 'medium',
534
+ category: 'ux',
535
+ description: 'No form validation feedback detected',
536
+ fixable: true,
537
+ suggestion: 'Add inline validation and error messages for form inputs',
538
+ });
539
+ }
540
+ if (!metrics.hasEmptyStates) {
541
+ issues.push({
542
+ severity: 'low',
543
+ category: 'ux',
544
+ description: 'No empty states detected',
545
+ fixable: true,
546
+ suggestion: 'Add empty states for lists, tables, and data displays',
547
+ });
548
+ }
549
+ return issues;
550
+ }
551
+ /**
552
+ * Generate UI/UX improvements
553
+ */
554
+ generateUiUxImprovements(metrics) {
555
+ const improvements = [];
556
+ if (!metrics.hasResponsiveDesign) {
557
+ improvements.push({
558
+ type: 'refactor',
559
+ description: 'Implement responsive design for mobile and tablet devices',
560
+ effort: 'significant',
561
+ impact: 'high',
562
+ steps: [
563
+ 'Add viewport meta tag',
564
+ 'Use responsive units (rem, %, vw/vh)',
565
+ 'Add media queries for breakpoints',
566
+ 'Test on multiple devices',
567
+ ],
568
+ });
569
+ }
570
+ if (!metrics.hasLoadingStates) {
571
+ improvements.push({
572
+ type: 'refactor',
573
+ description: 'Add loading states for better perceived performance',
574
+ effort: 'moderate',
575
+ impact: 'medium',
576
+ steps: [
577
+ 'Use skeleton screens for content loading',
578
+ 'Add spinners for actions',
579
+ 'Consider optimistic UI updates',
580
+ ],
581
+ });
582
+ }
583
+ if (!metrics.hasErrorHandling) {
584
+ improvements.push({
585
+ type: 'refactor',
586
+ description: 'Implement comprehensive error handling UX',
587
+ effort: 'moderate',
588
+ impact: 'high',
589
+ steps: [
590
+ 'Add error boundaries for React components',
591
+ 'Display user-friendly error messages',
592
+ 'Add retry mechanisms for failed operations',
593
+ 'Log errors for debugging',
594
+ ],
595
+ });
596
+ }
597
+ if (!metrics.hasFormValidation) {
598
+ improvements.push({
599
+ type: 'refactor',
600
+ description: 'Add form validation with clear feedback',
601
+ effort: 'moderate',
602
+ impact: 'medium',
603
+ steps: [
604
+ 'Validate on blur for inline feedback',
605
+ 'Show error messages below inputs',
606
+ 'Disable submit button until valid',
607
+ 'Use HTML5 validation attributes',
608
+ ],
609
+ });
610
+ }
611
+ if (!metrics.hasEmptyStates) {
612
+ improvements.push({
613
+ type: 'refactor',
614
+ description: 'Add empty states for better UX',
615
+ effort: 'quick',
616
+ impact: 'low',
617
+ steps: [
618
+ 'Create EmptyState component',
619
+ 'Add illustrations or icons',
620
+ 'Include clear messaging and actions',
621
+ ],
622
+ });
623
+ }
624
+ return improvements;
625
+ }
626
+ /**
627
+ * Calculate UI/UX score
628
+ */
629
+ calculateUiUxScore(metrics) {
630
+ let score = 0;
631
+ if (metrics.hasResponsiveDesign)
632
+ score += 25;
633
+ if (metrics.hasLoadingStates)
634
+ score += 20;
635
+ if (metrics.hasErrorHandling)
636
+ score += 25;
637
+ if (metrics.hasFormValidation)
638
+ score += 20;
639
+ if (metrics.hasEmptyStates)
640
+ score += 10;
641
+ return score;
642
+ }
643
+ /**
644
+ * Count passed checks for metadata
645
+ */
646
+ countPassedChecks(metrics) {
647
+ let count = 0;
648
+ if (metrics.hasResponsiveDesign)
649
+ count++;
650
+ if (metrics.hasLoadingStates)
651
+ count++;
652
+ if (metrics.hasErrorHandling)
653
+ count++;
654
+ if (metrics.hasFormValidation)
655
+ count++;
656
+ if (metrics.hasEmptyStates)
657
+ count++;
658
+ return count;
659
+ }
660
+ }
661
+ /**
662
+ * Default analyzer instance
663
+ */
664
+ export const uiUxAnalyzer = new UiUxAnalyzer();
665
+ //# sourceMappingURL=ui-ux.analyzer.js.map