apex-auditor 0.2.0 → 0.2.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 (2) hide show
  1. package/dist/cli.js +82 -7
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -47,6 +47,7 @@ function parseArgs(argv) {
47
47
  */
48
48
  export async function runAuditCli(argv) {
49
49
  const args = parseArgs(argv);
50
+ const startTimeMs = Date.now();
50
51
  const { configPath, config } = await loadConfig({ configPath: args.configPath });
51
52
  const effectiveLogLevel = args.logLevelOverride ?? config.logLevel;
52
53
  const effectiveConfig = {
@@ -66,6 +67,14 @@ export async function runAuditCli(argv) {
66
67
  console.log(consoleTable);
67
68
  printRedIssues(summary.results);
68
69
  printCiSummary(args, summary.results, effectiveConfig.budgets);
70
+ printLowestPerformancePages(summary.results, useColor);
71
+ const elapsedMs = Date.now() - startTimeMs;
72
+ const elapsedText = formatElapsedTime(elapsedMs);
73
+ const runsPerTarget = effectiveConfig.runs ?? 1;
74
+ const comboCount = summary.results.length;
75
+ const totalRuns = comboCount * runsPerTarget;
76
+ // eslint-disable-next-line no-console
77
+ console.log(`\nCompleted in ${elapsedText} (${comboCount} page/device combinations x ${runsPerTarget} runs = ${totalRuns} Lighthouse runs).`);
69
78
  }
70
79
  function buildMarkdown(results) {
71
80
  const header = [
@@ -77,11 +86,11 @@ function buildMarkdown(results) {
77
86
  }
78
87
  function buildConsoleTable(results, useColor) {
79
88
  const header = [
80
- "| Label | Path | Device | P | A | BP | SEO | LCP (s) | FCP (s) | TBT (ms) | CLS | Error | Top issues |",
81
- "|-------|------|--------|---|---|----|-----|---------|---------|----------|-----|-------|-----------|",
89
+ "| Label | Path | Device | P | A | BP | SEO | LCP (s) | FCP (s) | TBT (ms) | CLS |",
90
+ "|-------|------|--------|---|---|----|-----|---------|---------|----------|-----|",
82
91
  ].join("\n");
83
- const lines = results.map((result) => buildConsoleRow(result, useColor));
84
- return `${header}\n${lines.join("\n")}`;
92
+ const rows = results.map((result) => buildConsoleRow(result, useColor));
93
+ return `${header}\n${rows.join("\n")}`;
85
94
  }
86
95
  function buildRow(result) {
87
96
  const scores = result.scores;
@@ -95,19 +104,47 @@ function buildRow(result) {
95
104
  return `| ${result.label} | ${result.path} | ${result.device} | ${scores.performance ?? "-"} | ${scores.accessibility ?? "-"} | ${scores.bestPractices ?? "-"} | ${scores.seo ?? "-"} | ${lcpSeconds} | ${fcpSeconds} | ${tbtMs} | ${cls} | ${error} | ${issues} |`;
96
105
  }
97
106
  function buildConsoleRow(result, useColor) {
107
+ const line1 = buildConsoleRowLine1(result, useColor);
108
+ const line2 = buildConsoleRowLine2(result, useColor);
109
+ const line3 = buildConsoleRowLine3(result);
110
+ const lines = [line1];
111
+ if (line2.length > 0) {
112
+ lines.push(line2);
113
+ }
114
+ if (line3.length > 0) {
115
+ lines.push(line3);
116
+ }
117
+ return lines.join("\n");
118
+ }
119
+ function buildConsoleRowLine1(result, useColor) {
98
120
  const scores = result.scores;
99
121
  const metrics = result.metrics;
100
122
  const lcpSeconds = metrics.lcpMs !== undefined ? (metrics.lcpMs / 1000).toFixed(1) : "-";
101
123
  const fcpSeconds = metrics.fcpMs !== undefined ? (metrics.fcpMs / 1000).toFixed(1) : "-";
102
124
  const tbtMs = metrics.tbtMs !== undefined ? Math.round(metrics.tbtMs).toString() : "-";
103
125
  const cls = metrics.cls !== undefined ? metrics.cls.toFixed(3) : "-";
104
- const issues = formatTopIssues(result.opportunities);
105
- const error = result.runtimeErrorCode ?? (result.runtimeErrorMessage !== undefined ? result.runtimeErrorMessage : "");
106
126
  const performanceText = colourScore(scores.performance, useColor);
107
127
  const accessibilityText = colourScore(scores.accessibility, useColor);
108
128
  const bestPracticesText = colourScore(scores.bestPractices, useColor);
109
129
  const seoText = colourScore(scores.seo, useColor);
110
- return `| ${result.label} | ${result.path} | ${result.device} | ${performanceText} | ${accessibilityText} | ${bestPracticesText} | ${seoText} | ${lcpSeconds} | ${fcpSeconds} | ${tbtMs} | ${cls} | ${error} | ${issues} |`;
130
+ return `| ${result.label} | ${result.path} | ${result.device} | ${performanceText} | ${accessibilityText} | ${bestPracticesText} | ${seoText} | ${lcpSeconds} | ${fcpSeconds} | ${tbtMs} | ${cls} |`;
131
+ }
132
+ function buildConsoleRowLine2(result, useColor) {
133
+ const errorCode = result.runtimeErrorCode;
134
+ const errorMessage = result.runtimeErrorMessage;
135
+ if (!errorCode && !errorMessage) {
136
+ return "";
137
+ }
138
+ const errorText = errorCode ?? errorMessage ?? "";
139
+ const prefix = useColor ? `${ANSI_RED}↳ Error:${ANSI_RESET}` : "↳ Error:";
140
+ return ` ${prefix} ${errorText}`;
141
+ }
142
+ function buildConsoleRowLine3(result) {
143
+ const issues = formatTopIssues(result.opportunities);
144
+ if (issues.length === 0) {
145
+ return "";
146
+ }
147
+ return ` ↳ Top issues: ${issues}`;
111
148
  }
112
149
  function formatTopIssues(opportunities) {
113
150
  if (opportunities.length === 0) {
@@ -250,6 +287,44 @@ function addCategoryViolation(id, actual, limit, result, allViolations) {
250
287
  limit,
251
288
  });
252
289
  }
290
+ function formatElapsedTime(elapsedMs) {
291
+ const totalSeconds = Math.round(elapsedMs / 1000);
292
+ if (totalSeconds < 60) {
293
+ return `${totalSeconds}s`;
294
+ }
295
+ const minutes = Math.floor(totalSeconds / 60);
296
+ const remainingSeconds = totalSeconds % 60;
297
+ if (remainingSeconds === 0) {
298
+ return `${minutes}m`;
299
+ }
300
+ return `${minutes}m ${remainingSeconds}s`;
301
+ }
302
+ function printLowestPerformancePages(results, useColor) {
303
+ const entries = results.map((result) => ({
304
+ result,
305
+ performance: result.scores.performance,
306
+ }));
307
+ const definedEntries = entries
308
+ .filter((entry) => {
309
+ return typeof entry.performance === "number";
310
+ })
311
+ .sort((a, b) => a.performance - b.performance);
312
+ const limit = 5;
313
+ const worst = definedEntries.slice(0, limit);
314
+ if (worst.length === 0) {
315
+ return;
316
+ }
317
+ // eslint-disable-next-line no-console
318
+ console.log("\nLowest Performance pages:");
319
+ for (const entry of worst) {
320
+ const perfText = colourScore(entry.performance, useColor);
321
+ const label = entry.result.label;
322
+ const path = entry.result.path;
323
+ const device = entry.result.device;
324
+ // eslint-disable-next-line no-console
325
+ console.log(`- ${label} ${path} [${device}] P:${perfText}`);
326
+ }
327
+ }
253
328
  function collectMetricViolations(result, metricsBudgets, allViolations) {
254
329
  const metrics = result.metrics;
255
330
  addMetricViolation("lcpMs", metrics.lcpMs, metricsBudgets.lcpMs, result, allViolations);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "apex-auditor",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "private": false,
5
5
  "description": "CLI to run structured Lighthouse audits (Performance, Accessibility, Best Practices, SEO) across routes.",
6
6
  "type": "module",