@reliverse/dler 2.0.0 → 2.0.2

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 (135) hide show
  1. package/dist/cli.d.ts +2 -0
  2. package/dist/cli.js +3 -0
  3. package/dist/cmds/build/cmd.d.ts +2 -0
  4. package/dist/cmds/build/cmd.js +564 -0
  5. package/dist/cmds/clean/cmd.d.ts +2 -0
  6. package/dist/cmds/clean/cmd.js +146 -0
  7. package/dist/cmds/clean/impl.d.ts +2 -0
  8. package/dist/cmds/clean/impl.js +627 -0
  9. package/dist/cmds/clean/presets.d.ts +10 -0
  10. package/dist/cmds/clean/presets.js +112 -0
  11. package/dist/cmds/clean/types.d.ts +62 -0
  12. package/dist/cmds/clean/types.js +0 -0
  13. package/dist/cmds/init/cmd.d.ts +3 -0
  14. package/dist/cmds/init/cmd.js +56 -0
  15. package/dist/cmds/init/impl/config.d.ts +45 -0
  16. package/dist/cmds/init/impl/config.js +99 -0
  17. package/dist/cmds/init/impl/generators.d.ts +6 -0
  18. package/dist/cmds/init/impl/generators.js +178 -0
  19. package/dist/cmds/init/impl/prompts.d.ts +2 -0
  20. package/dist/cmds/init/impl/prompts.js +98 -0
  21. package/dist/cmds/init/impl/types.d.ts +22 -0
  22. package/dist/cmds/init/impl/types.js +0 -0
  23. package/dist/cmds/init/impl/utils.d.ts +4 -0
  24. package/dist/cmds/init/impl/utils.js +11 -0
  25. package/dist/cmds/init/impl/validators.d.ts +4 -0
  26. package/dist/cmds/init/impl/validators.js +42 -0
  27. package/dist/cmds/integrate/cmd.d.ts +3 -0
  28. package/dist/cmds/integrate/cmd.js +70 -0
  29. package/dist/cmds/integrate/impl.d.ts +7 -0
  30. package/dist/cmds/integrate/impl.js +127 -0
  31. package/dist/cmds/integrate/integrations/base.d.ts +13 -0
  32. package/dist/cmds/integrate/integrations/base.js +41 -0
  33. package/dist/cmds/integrate/integrations/nextjs.d.ts +16 -0
  34. package/dist/cmds/integrate/integrations/nextjs.js +167 -0
  35. package/dist/cmds/integrate/integrations/registry.d.ts +7 -0
  36. package/dist/cmds/integrate/integrations/registry.js +31 -0
  37. package/dist/cmds/integrate/integrations/ultracite.d.ts +11 -0
  38. package/dist/cmds/integrate/integrations/ultracite.js +40 -0
  39. package/dist/cmds/integrate/types.d.ts +39 -0
  40. package/dist/cmds/integrate/types.js +0 -0
  41. package/dist/cmds/integrate/utils/biome.d.ts +4 -0
  42. package/dist/cmds/integrate/utils/biome.js +140 -0
  43. package/dist/cmds/integrate/utils/context.d.ts +3 -0
  44. package/dist/cmds/integrate/utils/context.js +111 -0
  45. package/dist/cmds/integrate/utils/temp.d.ts +3 -0
  46. package/dist/cmds/integrate/utils/temp.js +36 -0
  47. package/dist/cmds/perf/analysis/bundle.d.ts +20 -0
  48. package/dist/cmds/perf/analysis/bundle.js +225 -0
  49. package/dist/cmds/perf/analysis/filesystem.d.ts +27 -0
  50. package/dist/cmds/perf/analysis/filesystem.js +246 -0
  51. package/dist/cmds/perf/analysis/monorepo.d.ts +29 -0
  52. package/dist/cmds/perf/analysis/monorepo.js +307 -0
  53. package/dist/cmds/perf/benchmarks/command.d.ts +21 -0
  54. package/dist/cmds/perf/benchmarks/command.js +162 -0
  55. package/dist/cmds/perf/benchmarks/memory.d.ts +41 -0
  56. package/dist/cmds/perf/benchmarks/memory.js +169 -0
  57. package/dist/cmds/perf/benchmarks/runner.d.ts +22 -0
  58. package/dist/cmds/perf/benchmarks/runner.js +157 -0
  59. package/dist/cmds/perf/cmd.d.ts +2 -0
  60. package/dist/cmds/perf/cmd.js +238 -0
  61. package/dist/cmds/perf/impl.d.ts +24 -0
  62. package/dist/cmds/perf/impl.js +304 -0
  63. package/dist/cmds/perf/reporters/console.d.ts +12 -0
  64. package/dist/cmds/perf/reporters/console.js +257 -0
  65. package/dist/cmds/perf/reporters/html.d.ts +27 -0
  66. package/dist/cmds/perf/reporters/html.js +881 -0
  67. package/dist/cmds/perf/reporters/json.d.ts +9 -0
  68. package/dist/cmds/perf/reporters/json.js +32 -0
  69. package/dist/cmds/perf/types.d.ts +184 -0
  70. package/dist/cmds/perf/types.js +0 -0
  71. package/dist/cmds/perf/utils/cache.d.ts +23 -0
  72. package/dist/cmds/perf/utils/cache.js +171 -0
  73. package/dist/cmds/perf/utils/formatter.d.ts +17 -0
  74. package/dist/cmds/perf/utils/formatter.js +134 -0
  75. package/dist/cmds/perf/utils/stats.d.ts +15 -0
  76. package/dist/cmds/perf/utils/stats.js +101 -0
  77. package/dist/cmds/publish/cmd.d.ts +3 -0
  78. package/dist/cmds/publish/cmd.js +189 -0
  79. package/dist/cmds/shell/cmd.d.ts +3 -0
  80. package/dist/cmds/shell/cmd.js +50 -0
  81. package/dist/cmds/tsc/cache.d.ts +27 -0
  82. package/dist/cmds/tsc/cache.js +160 -0
  83. package/dist/cmds/tsc/cmd.d.ts +2 -0
  84. package/dist/cmds/tsc/cmd.js +111 -0
  85. package/dist/cmds/tsc/impl.d.ts +41 -0
  86. package/dist/cmds/tsc/impl.js +572 -0
  87. package/dist/cmds/tsc/types.d.ts +57 -0
  88. package/dist/cmds/tsc/types.js +0 -0
  89. package/package.json +4 -11
  90. package/src/cli.ts +8 -0
  91. package/src/cmds/build/cmd.ts +582 -0
  92. package/src/cmds/clean/cmd.ts +166 -0
  93. package/src/cmds/clean/impl.ts +900 -0
  94. package/src/cmds/clean/presets.ts +158 -0
  95. package/src/cmds/clean/types.ts +71 -0
  96. package/src/cmds/init/cmd.ts +68 -0
  97. package/src/cmds/init/impl/config.ts +105 -0
  98. package/src/cmds/init/impl/generators.ts +220 -0
  99. package/src/cmds/init/impl/prompts.ts +137 -0
  100. package/src/cmds/init/impl/types.ts +25 -0
  101. package/src/cmds/init/impl/utils.ts +17 -0
  102. package/src/cmds/init/impl/validators.ts +55 -0
  103. package/src/cmds/integrate/cmd.ts +82 -0
  104. package/src/cmds/integrate/impl.ts +204 -0
  105. package/src/cmds/integrate/integrations/base.ts +69 -0
  106. package/src/cmds/integrate/integrations/nextjs.ts +227 -0
  107. package/src/cmds/integrate/integrations/registry.ts +45 -0
  108. package/src/cmds/integrate/integrations/ultracite.ts +53 -0
  109. package/src/cmds/integrate/types.ts +48 -0
  110. package/src/cmds/integrate/utils/biome.ts +173 -0
  111. package/src/cmds/integrate/utils/context.ts +148 -0
  112. package/src/cmds/integrate/utils/temp.ts +47 -0
  113. package/src/cmds/perf/analysis/bundle.ts +311 -0
  114. package/src/cmds/perf/analysis/filesystem.ts +324 -0
  115. package/src/cmds/perf/analysis/monorepo.ts +439 -0
  116. package/src/cmds/perf/benchmarks/command.ts +230 -0
  117. package/src/cmds/perf/benchmarks/memory.ts +249 -0
  118. package/src/cmds/perf/benchmarks/runner.ts +220 -0
  119. package/src/cmds/perf/cmd.ts +285 -0
  120. package/src/cmds/perf/impl.ts +411 -0
  121. package/src/cmds/perf/reporters/console.ts +331 -0
  122. package/src/cmds/perf/reporters/html.ts +984 -0
  123. package/src/cmds/perf/reporters/json.ts +42 -0
  124. package/src/cmds/perf/types.ts +220 -0
  125. package/src/cmds/perf/utils/cache.ts +234 -0
  126. package/src/cmds/perf/utils/formatter.ts +190 -0
  127. package/src/cmds/perf/utils/stats.ts +153 -0
  128. package/src/cmds/publish/cmd.ts +215 -0
  129. package/src/cmds/shell/cmd.ts +61 -0
  130. package/src/cmds/tsc/cache.ts +237 -0
  131. package/src/cmds/tsc/cmd.ts +139 -0
  132. package/src/cmds/tsc/impl.ts +855 -0
  133. package/src/cmds/tsc/types.ts +66 -0
  134. package/tsconfig.json +9 -0
  135. package/cli.js +0 -1316
@@ -0,0 +1,881 @@
1
+ import { writeFileSync } from "node:fs";
2
+ import { logger } from "@reliverse/dler-logger";
3
+ import {
4
+ formatBytes,
5
+ formatDuration,
6
+ formatPercentage
7
+ } from "../utils/formatter.js";
8
+ export class HtmlReporter {
9
+ outputPath;
10
+ constructor(outputPath) {
11
+ this.outputPath = outputPath;
12
+ }
13
+ report(report) {
14
+ const html = this.generateHtml(report);
15
+ if (this.outputPath) {
16
+ try {
17
+ writeFileSync(this.outputPath, html, "utf-8");
18
+ logger.success(`\u{1F310} HTML report saved to: ${this.outputPath}`);
19
+ } catch (error) {
20
+ logger.error(`Failed to save HTML report: ${error}`);
21
+ }
22
+ } else {
23
+ console.log(html);
24
+ }
25
+ }
26
+ generateHtml(report) {
27
+ const timestamp = new Date(report.timestamp).toLocaleString();
28
+ return `<!DOCTYPE html>
29
+ <html lang="en">
30
+ <head>
31
+ <meta charset="UTF-8">
32
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
33
+ <title>Performance Analysis Report</title>
34
+ <style>
35
+ ${this.getStyles()}
36
+ </style>
37
+ </head>
38
+ <body>
39
+ <div class="container">
40
+ <header>
41
+ <h1>\u{1F4CA} Performance Analysis Report</h1>
42
+ <p class="timestamp">Generated: ${timestamp}</p>
43
+ </header>
44
+
45
+ <main>
46
+ ${this.generateBenchmarkSection(report.benchmark)}
47
+ ${this.generateBundleAnalysisSection(report.bundleAnalysis)}
48
+ ${this.generateFileSystemAnalysisSection(report.fileSystemAnalysis)}
49
+ ${this.generateMonorepoAnalysisSection(report.monorepoAnalysis)}
50
+ ${this.generateBaselineComparisonSection(report.baseline)}
51
+ </main>
52
+
53
+ <footer>
54
+ <p>Generated by dler perf</p>
55
+ </footer>
56
+ </div>
57
+
58
+ <script>
59
+ ${this.getScripts()}
60
+ <\/script>
61
+ </body>
62
+ </html>`;
63
+ }
64
+ generateBenchmarkSection(benchmark) {
65
+ if (!benchmark) return "";
66
+ return `
67
+ <section class="section">
68
+ <h2>\u{1F680} Command Benchmark Results</h2>
69
+ <div class="summary">
70
+ <div class="metric">
71
+ <span class="label">Command:</span>
72
+ <span class="value">${benchmark.command}</span>
73
+ </div>
74
+ <div class="metric">
75
+ <span class="label">Runs:</span>
76
+ <span class="value">${benchmark.runs} (${benchmark.warmup} warmup)</span>
77
+ </div>
78
+ <div class="metric">
79
+ <span class="label">Concurrency:</span>
80
+ <span class="value">${benchmark.concurrency}</span>
81
+ </div>
82
+ <div class="metric">
83
+ <span class="label">Success:</span>
84
+ <span class="value ${benchmark.success ? "success" : "error"}">${benchmark.success ? "\u2705" : "\u274C"}</span>
85
+ </div>
86
+ </div>
87
+
88
+ <div class="stats-grid">
89
+ <div class="stat-card">
90
+ <h3>\u23F1\uFE0F Timing Statistics</h3>
91
+ <div class="stat-list">
92
+ <div class="stat-item">
93
+ <span>Mean:</span>
94
+ <span>${formatDuration(benchmark.statistics.mean)}</span>
95
+ </div>
96
+ <div class="stat-item">
97
+ <span>Median:</span>
98
+ <span>${formatDuration(benchmark.statistics.median)}</span>
99
+ </div>
100
+ <div class="stat-item">
101
+ <span>Min:</span>
102
+ <span>${formatDuration(benchmark.statistics.min)}</span>
103
+ </div>
104
+ <div class="stat-item">
105
+ <span>Max:</span>
106
+ <span>${formatDuration(benchmark.statistics.max)}</span>
107
+ </div>
108
+ <div class="stat-item">
109
+ <span>P95:</span>
110
+ <span>${formatDuration(benchmark.statistics.p95)}</span>
111
+ </div>
112
+ <div class="stat-item">
113
+ <span>P99:</span>
114
+ <span>${formatDuration(benchmark.statistics.p99)}</span>
115
+ </div>
116
+ </div>
117
+ </div>
118
+
119
+ <div class="stat-card">
120
+ <h3>\u{1F4BE} Memory Statistics</h3>
121
+ <div class="stat-list">
122
+ <div class="stat-item">
123
+ <span>Peak RSS:</span>
124
+ <span>${formatBytes(benchmark.memory.peak.rss)}</span>
125
+ </div>
126
+ <div class="stat-item">
127
+ <span>Avg RSS:</span>
128
+ <span>${formatBytes(benchmark.memory.average.rss)}</span>
129
+ </div>
130
+ <div class="stat-item">
131
+ <span>Peak Heap:</span>
132
+ <span>${formatBytes(benchmark.memory.peak.heapUsed)}</span>
133
+ </div>
134
+ <div class="stat-item">
135
+ <span>Avg Heap:</span>
136
+ <span>${formatBytes(benchmark.memory.average.heapUsed)}</span>
137
+ </div>
138
+ <div class="stat-item">
139
+ <span>Growth:</span>
140
+ <span>${formatBytes(benchmark.memory.growth)}</span>
141
+ </div>
142
+ </div>
143
+ </div>
144
+ </div>
145
+
146
+ ${this.generateTimingChart(benchmark)}
147
+ </section>`;
148
+ }
149
+ generateBundleAnalysisSection(analysis) {
150
+ if (!analysis) return "";
151
+ return `
152
+ <section class="section">
153
+ <h2>\u{1F4E6} Bundle Analysis Results</h2>
154
+ <div class="summary">
155
+ <div class="metric">
156
+ <span class="label">Target:</span>
157
+ <span class="value">${analysis.target}</span>
158
+ </div>
159
+ <div class="metric">
160
+ <span class="label">Total Size:</span>
161
+ <span class="value">${formatBytes(analysis.totalSize)}</span>
162
+ </div>
163
+ <div class="metric">
164
+ <span class="label">File Count:</span>
165
+ <span class="value">${analysis.fileCount.toLocaleString()}</span>
166
+ </div>
167
+ <div class="metric">
168
+ <span class="label">Compression Potential:</span>
169
+ <span class="value">${analysis.compressionPotential.toFixed(1)}%</span>
170
+ </div>
171
+ </div>
172
+
173
+ ${this.generateFileTable("Largest Files", analysis.largestFiles, ["File", "Size", "Percentage", "Type"])}
174
+ ${this.generateModuleTable("Top Modules", analysis.modules, ["Module", "Size", "Percentage", "Type"])}
175
+ ${this.generateDuplicateTable("Duplicate Dependencies", analysis.duplicates, ["Module", "Count", "Total Size", "Locations"])}
176
+ </section>`;
177
+ }
178
+ generateFileSystemAnalysisSection(analysis) {
179
+ if (!analysis) return "";
180
+ return `
181
+ <section class="section">
182
+ <h2>\u{1F4C1} File System Analysis Results</h2>
183
+ <div class="summary">
184
+ <div class="metric">
185
+ <span class="label">Target:</span>
186
+ <span class="value">${analysis.target}</span>
187
+ </div>
188
+ <div class="metric">
189
+ <span class="label">Total Files:</span>
190
+ <span class="value">${analysis.totalFiles.toLocaleString()}</span>
191
+ </div>
192
+ <div class="metric">
193
+ <span class="label">Total Size:</span>
194
+ <span class="value">${formatBytes(analysis.totalSize)}</span>
195
+ </div>
196
+ <div class="metric">
197
+ <span class="label">Directories:</span>
198
+ <span class="value">${analysis.directoryCount.toLocaleString()}</span>
199
+ </div>
200
+ <div class="metric">
201
+ <span class="label">Max Depth:</span>
202
+ <span class="value">${analysis.maxDepth}</span>
203
+ </div>
204
+ <div class="metric">
205
+ <span class="label">Compression Potential:</span>
206
+ <span class="value">${analysis.compressionPotential.toFixed(1)}%</span>
207
+ </div>
208
+ </div>
209
+
210
+ ${this.generateFileTable("Largest Files", analysis.largestFiles, ["File", "Size", "Percentage", "Type"])}
211
+ ${this.generateDirectoryTable("Largest Directories", analysis.largestDirectories, ["Directory", "Size", "Files", "Depth"])}
212
+ ${this.generateFileTypeTable("File Type Distribution", analysis.fileTypes, ["Type", "Count", "Size", "Percentage"])}
213
+ </section>`;
214
+ }
215
+ generateMonorepoAnalysisSection(analysis) {
216
+ if (!analysis) return "";
217
+ return `
218
+ <section class="section">
219
+ <h2>\u{1F3D7}\uFE0F Monorepo Analysis Results</h2>
220
+ <div class="summary">
221
+ <div class="metric">
222
+ <span class="label">Packages:</span>
223
+ <span class="value">${analysis.packages.length.toLocaleString()}</span>
224
+ </div>
225
+ <div class="metric">
226
+ <span class="label">Dependencies:</span>
227
+ <span class="value">${analysis.dependencies.edges.length.toLocaleString()}</span>
228
+ </div>
229
+ <div class="metric">
230
+ <span class="label">Circular Dependencies:</span>
231
+ <span class="value">${analysis.circularDependencies.length}</span>
232
+ </div>
233
+ <div class="metric">
234
+ <span class="label">Suggested Concurrency:</span>
235
+ <span class="value">${analysis.suggestedConcurrency}</span>
236
+ </div>
237
+ </div>
238
+
239
+ ${this.generateBuildOrderSection(analysis)}
240
+ ${this.generateCircularDependenciesSection(analysis.circularDependencies)}
241
+ ${this.generateBottlenecksSection(analysis.bottlenecks)}
242
+ </section>`;
243
+ }
244
+ generateBaselineComparisonSection(baseline) {
245
+ if (!baseline?.exists) return "";
246
+ return `
247
+ <section class="section">
248
+ <h2>\u{1F4C8} Baseline Comparison</h2>
249
+ <div class="comparison">
250
+ ${baseline.improvement !== void 0 ? `
251
+ <div class="metric">
252
+ <span class="label">Performance:</span>
253
+ <span class="value improvement">+${baseline.improvement.toFixed(2)}%</span>
254
+ </div>
255
+ ` : ""}
256
+ ${baseline.regression !== void 0 ? `
257
+ <div class="metric">
258
+ <span class="label">Performance:</span>
259
+ <span class="value regression">-${baseline.regression.toFixed(2)}%</span>
260
+ </div>
261
+ ` : ""}
262
+ </div>
263
+ </section>`;
264
+ }
265
+ generateFileTable(title, files, headers) {
266
+ if (files.length === 0) return "";
267
+ return `
268
+ <div class="table-section">
269
+ <h3>${title}</h3>
270
+ <div class="table-container">
271
+ <table>
272
+ <thead>
273
+ <tr>
274
+ ${headers.map((h) => `<th>${h}</th>`).join("")}
275
+ </tr>
276
+ </thead>
277
+ <tbody>
278
+ ${files.map(
279
+ (file) => `
280
+ <tr>
281
+ <td class="file-path">${this.truncatePath(file.path || file.name, 50)}</td>
282
+ <td>${formatBytes(file.size)}</td>
283
+ <td>${formatPercentage(file.percentage || 0, 100)}</td>
284
+ <td>${file.type || (file.isExternal ? "External" : "Internal")}</td>
285
+ </tr>
286
+ `
287
+ ).join("")}
288
+ </tbody>
289
+ </table>
290
+ </div>
291
+ </div>`;
292
+ }
293
+ generateModuleTable(title, modules, headers) {
294
+ if (modules.length === 0) return "";
295
+ return `
296
+ <div class="table-section">
297
+ <h3>${title}</h3>
298
+ <div class="table-container">
299
+ <table>
300
+ <thead>
301
+ <tr>
302
+ ${headers.map((h) => `<th>${h}</th>`).join("")}
303
+ </tr>
304
+ </thead>
305
+ <tbody>
306
+ ${modules.map(
307
+ (module) => `
308
+ <tr>
309
+ <td class="file-path">${this.truncatePath(module.name, 50)}</td>
310
+ <td>${formatBytes(module.size)}</td>
311
+ <td>${formatPercentage(module.percentage || 0, 100)}</td>
312
+ <td>${module.isExternal ? "External" : "Internal"}</td>
313
+ </tr>
314
+ `
315
+ ).join("")}
316
+ </tbody>
317
+ </table>
318
+ </div>
319
+ </div>`;
320
+ }
321
+ generateDuplicateTable(title, duplicates, headers) {
322
+ if (duplicates.length === 0) return "";
323
+ return `
324
+ <div class="table-section">
325
+ <h3>${title}</h3>
326
+ <div class="table-container">
327
+ <table>
328
+ <thead>
329
+ <tr>
330
+ ${headers.map((h) => `<th>${h}</th>`).join("")}
331
+ </tr>
332
+ </thead>
333
+ <tbody>
334
+ ${duplicates.map(
335
+ (dup) => `
336
+ <tr>
337
+ <td class="file-path">${this.truncatePath(dup.name, 30)}</td>
338
+ <td>${dup.count}</td>
339
+ <td>${formatBytes(dup.totalSize)}</td>
340
+ <td>${dup.locations.length}</td>
341
+ </tr>
342
+ `
343
+ ).join("")}
344
+ </tbody>
345
+ </table>
346
+ </div>
347
+ </div>`;
348
+ }
349
+ generateDirectoryTable(title, directories, headers) {
350
+ if (directories.length === 0) return "";
351
+ return `
352
+ <div class="table-section">
353
+ <h3>${title}</h3>
354
+ <div class="table-container">
355
+ <table>
356
+ <thead>
357
+ <tr>
358
+ ${headers.map((h) => `<th>${h}</th>`).join("")}
359
+ </tr>
360
+ </thead>
361
+ <tbody>
362
+ ${directories.map(
363
+ (dir) => `
364
+ <tr>
365
+ <td class="file-path">${this.truncatePath(dir.path, 50)}</td>
366
+ <td>${formatBytes(dir.size)}</td>
367
+ <td>${dir.fileCount.toLocaleString()}</td>
368
+ <td>${dir.depth}</td>
369
+ </tr>
370
+ `
371
+ ).join("")}
372
+ </tbody>
373
+ </table>
374
+ </div>
375
+ </div>`;
376
+ }
377
+ generateFileTypeTable(title, types, headers) {
378
+ if (types.length === 0) return "";
379
+ return `
380
+ <div class="table-section">
381
+ <h3>${title}</h3>
382
+ <div class="table-container">
383
+ <table>
384
+ <thead>
385
+ <tr>
386
+ ${headers.map((h) => `<th>${h}</th>`).join("")}
387
+ </tr>
388
+ </thead>
389
+ <tbody>
390
+ ${types.map(
391
+ (type) => `
392
+ <tr>
393
+ <td>${type.extension || "no-extension"}</td>
394
+ <td>${type.count.toLocaleString()}</td>
395
+ <td>${formatBytes(type.totalSize)}</td>
396
+ <td>${formatPercentage(type.percentage || 0, 100)}</td>
397
+ </tr>
398
+ `
399
+ ).join("")}
400
+ </tbody>
401
+ </table>
402
+ </div>
403
+ </div>`;
404
+ }
405
+ generateBuildOrderSection(analysis) {
406
+ if (!analysis?.dependencies.levels.length) return "";
407
+ return `
408
+ <div class="table-section">
409
+ <h3>\u{1F504} Build Order</h3>
410
+ <div class="build-order">
411
+ ${analysis.dependencies.levels.map(
412
+ (level, i) => `
413
+ <div class="build-level">
414
+ <h4>Level ${i + 1}</h4>
415
+ <div class="package-list">
416
+ ${level.map((pkg) => `<span class="package">${pkg}</span>`).join("")}
417
+ </div>
418
+ </div>
419
+ `
420
+ ).join("")}
421
+ </div>
422
+ </div>`;
423
+ }
424
+ generateCircularDependenciesSection(circular) {
425
+ if (circular.length === 0) return "";
426
+ return `
427
+ <div class="table-section">
428
+ <h3>\u{1F504} Circular Dependencies</h3>
429
+ <div class="circular-deps">
430
+ ${circular.map(
431
+ (circ) => `
432
+ <div class="circular-item ${circ.severity}">
433
+ <span class="severity">${circ.severity.toUpperCase()}</span>
434
+ <span class="cycle">${circ.cycle.join(" \u2192 ")}</span>
435
+ </div>
436
+ `
437
+ ).join("")}
438
+ </div>
439
+ </div>`;
440
+ }
441
+ generateBottlenecksSection(bottlenecks) {
442
+ if (bottlenecks.length === 0) return "";
443
+ return `
444
+ <div class="table-section">
445
+ <h3>\u26A0\uFE0F Bottlenecks</h3>
446
+ <div class="table-container">
447
+ <table>
448
+ <thead>
449
+ <tr>
450
+ <th>Package</th>
451
+ <th>Type</th>
452
+ <th>Impact</th>
453
+ <th>Suggestion</th>
454
+ </tr>
455
+ </thead>
456
+ <tbody>
457
+ ${bottlenecks.map(
458
+ (bottleneck) => `
459
+ <tr>
460
+ <td>${bottleneck.package}</td>
461
+ <td>${bottleneck.type}</td>
462
+ <td>${bottleneck.impact}</td>
463
+ <td>${bottleneck.suggestion}</td>
464
+ </tr>
465
+ `
466
+ ).join("")}
467
+ </tbody>
468
+ </table>
469
+ </div>
470
+ </div>`;
471
+ }
472
+ generateTimingChart(benchmark) {
473
+ if (!benchmark) return "";
474
+ const measurements = benchmark.measurements.filter((m) => m.success);
475
+ if (measurements.length === 0) return "";
476
+ const maxDuration = Math.max(...measurements.map((m) => m.duration));
477
+ return `
478
+ <div class="chart-section">
479
+ <h3>\u{1F4CA} Timing Distribution</h3>
480
+ <div class="timing-chart">
481
+ ${measurements.map(
482
+ (m, i) => `
483
+ <div class="timing-bar">
484
+ <span class="run-number">${i + 1}</span>
485
+ <div class="bar-container">
486
+ <div class="bar" style="width: ${m.duration / maxDuration * 100}%"></div>
487
+ <span class="bar-value">${formatDuration(m.duration)}</span>
488
+ </div>
489
+ </div>
490
+ `
491
+ ).join("")}
492
+ </div>
493
+ </div>`;
494
+ }
495
+ truncatePath(path, maxLength) {
496
+ if (path.length <= maxLength) return path;
497
+ const start = path.substring(0, Math.floor(maxLength / 2) - 2);
498
+ const end = path.substring(path.length - Math.floor(maxLength / 2) + 2);
499
+ return `${start}...${end}`;
500
+ }
501
+ getStyles() {
502
+ return `
503
+ * {
504
+ margin: 0;
505
+ padding: 0;
506
+ box-sizing: border-box;
507
+ }
508
+
509
+ body {
510
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
511
+ line-height: 1.6;
512
+ color: #333;
513
+ background: #f8f9fa;
514
+ }
515
+
516
+ .container {
517
+ max-width: 1200px;
518
+ margin: 0 auto;
519
+ padding: 20px;
520
+ }
521
+
522
+ header {
523
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
524
+ color: white;
525
+ padding: 30px;
526
+ border-radius: 10px;
527
+ margin-bottom: 30px;
528
+ text-align: center;
529
+ }
530
+
531
+ header h1 {
532
+ font-size: 2.5em;
533
+ margin-bottom: 10px;
534
+ }
535
+
536
+ .timestamp {
537
+ opacity: 0.9;
538
+ font-size: 1.1em;
539
+ }
540
+
541
+ .section {
542
+ background: white;
543
+ padding: 30px;
544
+ border-radius: 10px;
545
+ margin-bottom: 30px;
546
+ box-shadow: 0 2px 10px rgba(0,0,0,0.1);
547
+ }
548
+
549
+ .section h2 {
550
+ color: #2c3e50;
551
+ margin-bottom: 20px;
552
+ font-size: 1.8em;
553
+ }
554
+
555
+ .summary {
556
+ display: grid;
557
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
558
+ gap: 20px;
559
+ margin-bottom: 30px;
560
+ }
561
+
562
+ .metric {
563
+ display: flex;
564
+ justify-content: space-between;
565
+ align-items: center;
566
+ padding: 15px;
567
+ background: #f8f9fa;
568
+ border-radius: 8px;
569
+ border-left: 4px solid #667eea;
570
+ }
571
+
572
+ .metric .label {
573
+ font-weight: 600;
574
+ color: #555;
575
+ }
576
+
577
+ .metric .value {
578
+ font-weight: 700;
579
+ color: #2c3e50;
580
+ }
581
+
582
+ .metric .value.success {
583
+ color: #27ae60;
584
+ }
585
+
586
+ .metric .value.error {
587
+ color: #e74c3c;
588
+ }
589
+
590
+ .metric .value.improvement {
591
+ color: #27ae60;
592
+ }
593
+
594
+ .metric .value.regression {
595
+ color: #e74c3c;
596
+ }
597
+
598
+ .stats-grid {
599
+ display: grid;
600
+ grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
601
+ gap: 30px;
602
+ margin-bottom: 30px;
603
+ }
604
+
605
+ .stat-card {
606
+ background: #f8f9fa;
607
+ padding: 20px;
608
+ border-radius: 8px;
609
+ border: 1px solid #e9ecef;
610
+ }
611
+
612
+ .stat-card h3 {
613
+ color: #2c3e50;
614
+ margin-bottom: 15px;
615
+ font-size: 1.3em;
616
+ }
617
+
618
+ .stat-list {
619
+ display: flex;
620
+ flex-direction: column;
621
+ gap: 10px;
622
+ }
623
+
624
+ .stat-item {
625
+ display: flex;
626
+ justify-content: space-between;
627
+ align-items: center;
628
+ padding: 8px 0;
629
+ border-bottom: 1px solid #e9ecef;
630
+ }
631
+
632
+ .stat-item:last-child {
633
+ border-bottom: none;
634
+ }
635
+
636
+ .table-section {
637
+ margin-bottom: 30px;
638
+ }
639
+
640
+ .table-section h3 {
641
+ color: #2c3e50;
642
+ margin-bottom: 15px;
643
+ font-size: 1.3em;
644
+ }
645
+
646
+ .table-container {
647
+ overflow-x: auto;
648
+ }
649
+
650
+ table {
651
+ width: 100%;
652
+ border-collapse: collapse;
653
+ background: white;
654
+ border-radius: 8px;
655
+ overflow: hidden;
656
+ box-shadow: 0 2px 10px rgba(0,0,0,0.1);
657
+ }
658
+
659
+ th {
660
+ background: #667eea;
661
+ color: white;
662
+ padding: 15px;
663
+ text-align: left;
664
+ font-weight: 600;
665
+ }
666
+
667
+ td {
668
+ padding: 12px 15px;
669
+ border-bottom: 1px solid #e9ecef;
670
+ }
671
+
672
+ tr:hover {
673
+ background: #f8f9fa;
674
+ }
675
+
676
+ .file-path {
677
+ font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
678
+ font-size: 0.9em;
679
+ color: #666;
680
+ }
681
+
682
+ .build-order {
683
+ display: flex;
684
+ flex-direction: column;
685
+ gap: 20px;
686
+ }
687
+
688
+ .build-level {
689
+ background: #f8f9fa;
690
+ padding: 20px;
691
+ border-radius: 8px;
692
+ border-left: 4px solid #667eea;
693
+ }
694
+
695
+ .build-level h4 {
696
+ color: #2c3e50;
697
+ margin-bottom: 10px;
698
+ }
699
+
700
+ .package-list {
701
+ display: flex;
702
+ flex-wrap: wrap;
703
+ gap: 10px;
704
+ }
705
+
706
+ .package {
707
+ background: #667eea;
708
+ color: white;
709
+ padding: 5px 12px;
710
+ border-radius: 20px;
711
+ font-size: 0.9em;
712
+ font-weight: 500;
713
+ }
714
+
715
+ .circular-deps {
716
+ display: flex;
717
+ flex-direction: column;
718
+ gap: 15px;
719
+ }
720
+
721
+ .circular-item {
722
+ display: flex;
723
+ align-items: center;
724
+ gap: 15px;
725
+ padding: 15px;
726
+ border-radius: 8px;
727
+ border-left: 4px solid #e74c3c;
728
+ }
729
+
730
+ .circular-item.low {
731
+ border-left-color: #f39c12;
732
+ }
733
+
734
+ .circular-item.medium {
735
+ border-left-color: #e67e22;
736
+ }
737
+
738
+ .circular-item.high {
739
+ border-left-color: #e74c3c;
740
+ }
741
+
742
+ .severity {
743
+ background: #e74c3c;
744
+ color: white;
745
+ padding: 4px 8px;
746
+ border-radius: 4px;
747
+ font-size: 0.8em;
748
+ font-weight: 600;
749
+ text-transform: uppercase;
750
+ }
751
+
752
+ .circular-item.low .severity {
753
+ background: #f39c12;
754
+ }
755
+
756
+ .circular-item.medium .severity {
757
+ background: #e67e22;
758
+ }
759
+
760
+ .cycle {
761
+ font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
762
+ font-size: 0.9em;
763
+ color: #666;
764
+ }
765
+
766
+ .chart-section {
767
+ margin-top: 30px;
768
+ }
769
+
770
+ .chart-section h3 {
771
+ color: #2c3e50;
772
+ margin-bottom: 15px;
773
+ font-size: 1.3em;
774
+ }
775
+
776
+ .timing-chart {
777
+ background: #f8f9fa;
778
+ padding: 20px;
779
+ border-radius: 8px;
780
+ }
781
+
782
+ .timing-bar {
783
+ display: flex;
784
+ align-items: center;
785
+ gap: 15px;
786
+ margin-bottom: 10px;
787
+ }
788
+
789
+ .run-number {
790
+ min-width: 30px;
791
+ font-weight: 600;
792
+ color: #666;
793
+ }
794
+
795
+ .bar-container {
796
+ flex: 1;
797
+ position: relative;
798
+ height: 25px;
799
+ background: #e9ecef;
800
+ border-radius: 12px;
801
+ overflow: hidden;
802
+ }
803
+
804
+ .bar {
805
+ height: 100%;
806
+ background: linear-gradient(90deg, #667eea, #764ba2);
807
+ border-radius: 12px;
808
+ transition: width 0.3s ease;
809
+ }
810
+
811
+ .bar-value {
812
+ position: absolute;
813
+ right: 10px;
814
+ top: 50%;
815
+ transform: translateY(-50%);
816
+ font-size: 0.9em;
817
+ font-weight: 600;
818
+ color: #2c3e50;
819
+ }
820
+
821
+ footer {
822
+ text-align: center;
823
+ padding: 20px;
824
+ color: #666;
825
+ font-size: 0.9em;
826
+ }
827
+
828
+ @media (max-width: 768px) {
829
+ .container {
830
+ padding: 10px;
831
+ }
832
+
833
+ .summary {
834
+ grid-template-columns: 1fr;
835
+ }
836
+
837
+ .stats-grid {
838
+ grid-template-columns: 1fr;
839
+ }
840
+
841
+ .package-list {
842
+ flex-direction: column;
843
+ }
844
+ }
845
+ `;
846
+ }
847
+ getScripts() {
848
+ return `
849
+ // Add any interactive functionality here
850
+ document.addEventListener('DOMContentLoaded', function() {
851
+ // Add smooth scrolling for better UX
852
+ document.querySelectorAll('a[href^="#"]').forEach(anchor => {
853
+ anchor.addEventListener('click', function (e) {
854
+ e.preventDefault();
855
+ document.querySelector(this.getAttribute('href')).scrollIntoView({
856
+ behavior: 'smooth'
857
+ });
858
+ });
859
+ });
860
+
861
+ // Add tooltips for truncated paths
862
+ document.querySelectorAll('.file-path').forEach(element => {
863
+ if (element.textContent.length > 50) {
864
+ element.title = element.textContent;
865
+ }
866
+ });
867
+ });
868
+ `;
869
+ }
870
+ static save(report, outputPath) {
871
+ const reporter = new HtmlReporter(outputPath);
872
+ reporter.report(report);
873
+ }
874
+ static print(report) {
875
+ const reporter = new HtmlReporter();
876
+ reporter.report(report);
877
+ }
878
+ }
879
+ export const createHtmlReporter = (outputPath) => {
880
+ return new HtmlReporter(outputPath);
881
+ };