apex-auditor 0.2.2 → 0.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +125 -26
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -6,6 +6,16 @@ const ANSI_RESET = "\u001B[0m";
|
|
|
6
6
|
const ANSI_RED = "\u001B[31m";
|
|
7
7
|
const ANSI_YELLOW = "\u001B[33m";
|
|
8
8
|
const ANSI_GREEN = "\u001B[32m";
|
|
9
|
+
const ANSI_CYAN = "\u001B[36m";
|
|
10
|
+
const ANSI_BLUE = "\u001B[34m";
|
|
11
|
+
const LCP_GOOD_MS = 2500;
|
|
12
|
+
const LCP_WARN_MS = 4000;
|
|
13
|
+
const FCP_GOOD_MS = 1800;
|
|
14
|
+
const FCP_WARN_MS = 3000;
|
|
15
|
+
const TBT_GOOD_MS = 200;
|
|
16
|
+
const TBT_WARN_MS = 600;
|
|
17
|
+
const CLS_GOOD = 0.1;
|
|
18
|
+
const CLS_WARN = 0.25;
|
|
9
19
|
function parseArgs(argv) {
|
|
10
20
|
let configPath;
|
|
11
21
|
let ci = false;
|
|
@@ -70,11 +80,12 @@ export async function runAuditCli(argv) {
|
|
|
70
80
|
printLowestPerformancePages(summary.results, useColor);
|
|
71
81
|
const elapsedMs = Date.now() - startTimeMs;
|
|
72
82
|
const elapsedText = formatElapsedTime(elapsedMs);
|
|
83
|
+
const elapsedDisplay = useColor ? `${ANSI_CYAN}${elapsedText}${ANSI_RESET}` : elapsedText;
|
|
73
84
|
const runsPerTarget = effectiveConfig.runs ?? 1;
|
|
74
85
|
const comboCount = summary.results.length;
|
|
75
86
|
const totalRuns = comboCount * runsPerTarget;
|
|
76
87
|
// eslint-disable-next-line no-console
|
|
77
|
-
console.log(`\nCompleted in ${
|
|
88
|
+
console.log(`\nCompleted in ${elapsedDisplay} (${comboCount} page/device combinations x ${runsPerTarget} runs = ${totalRuns} Lighthouse runs).`);
|
|
78
89
|
}
|
|
79
90
|
function buildMarkdown(results) {
|
|
80
91
|
const header = [
|
|
@@ -89,7 +100,16 @@ function buildConsoleTable(results, useColor) {
|
|
|
89
100
|
"| Label | Path | Device | P | A | BP | SEO | LCP (s) | FCP (s) | TBT (ms) | CLS |",
|
|
90
101
|
"|-------|------|--------|---|---|----|-----|---------|---------|----------|-----|",
|
|
91
102
|
].join("\n");
|
|
92
|
-
const rows =
|
|
103
|
+
const rows = [];
|
|
104
|
+
let previousKey;
|
|
105
|
+
for (const result of results) {
|
|
106
|
+
const key = `${result.label}:::${result.path}`;
|
|
107
|
+
if (previousKey !== undefined && key !== previousKey) {
|
|
108
|
+
rows.push("");
|
|
109
|
+
}
|
|
110
|
+
rows.push(buildConsoleRow(result, useColor));
|
|
111
|
+
previousKey = key;
|
|
112
|
+
}
|
|
93
113
|
return `${header}\n${rows.join("\n")}`;
|
|
94
114
|
}
|
|
95
115
|
function buildRow(result) {
|
|
@@ -104,32 +124,37 @@ function buildRow(result) {
|
|
|
104
124
|
return `| ${result.label} | ${result.path} | ${result.device} | ${scores.performance ?? "-"} | ${scores.accessibility ?? "-"} | ${scores.bestPractices ?? "-"} | ${scores.seo ?? "-"} | ${lcpSeconds} | ${fcpSeconds} | ${tbtMs} | ${cls} | ${error} | ${issues} |`;
|
|
105
125
|
}
|
|
106
126
|
function buildConsoleRow(result, useColor) {
|
|
107
|
-
const
|
|
108
|
-
const
|
|
109
|
-
const
|
|
110
|
-
const
|
|
111
|
-
|
|
112
|
-
|
|
127
|
+
const scoreLine = buildConsoleScoreLine(result, useColor);
|
|
128
|
+
const metricsLine = buildConsoleMetricsLine(result, useColor);
|
|
129
|
+
const errorLine = buildConsoleErrorLine(result, useColor);
|
|
130
|
+
const issuesLine = buildConsoleIssuesLine(result);
|
|
131
|
+
const lines = [scoreLine, metricsLine];
|
|
132
|
+
if (errorLine.length > 0) {
|
|
133
|
+
lines.push(errorLine);
|
|
113
134
|
}
|
|
114
|
-
if (
|
|
115
|
-
lines.push(
|
|
135
|
+
if (issuesLine.length > 0) {
|
|
136
|
+
lines.push(issuesLine);
|
|
116
137
|
}
|
|
117
138
|
return lines.join("\n");
|
|
118
139
|
}
|
|
119
|
-
function
|
|
140
|
+
function buildConsoleScoreLine(result, useColor) {
|
|
120
141
|
const scores = result.scores;
|
|
121
|
-
const metrics = result.metrics;
|
|
122
|
-
const lcpSeconds = metrics.lcpMs !== undefined ? (metrics.lcpMs / 1000).toFixed(1) : "-";
|
|
123
|
-
const fcpSeconds = metrics.fcpMs !== undefined ? (metrics.fcpMs / 1000).toFixed(1) : "-";
|
|
124
|
-
const tbtMs = metrics.tbtMs !== undefined ? Math.round(metrics.tbtMs).toString() : "-";
|
|
125
|
-
const cls = metrics.cls !== undefined ? metrics.cls.toFixed(3) : "-";
|
|
126
142
|
const performanceText = colourScore(scores.performance, useColor);
|
|
127
143
|
const accessibilityText = colourScore(scores.accessibility, useColor);
|
|
128
144
|
const bestPracticesText = colourScore(scores.bestPractices, useColor);
|
|
129
145
|
const seoText = colourScore(scores.seo, useColor);
|
|
130
|
-
|
|
146
|
+
const deviceText = formatDeviceLabel(result.device, useColor);
|
|
147
|
+
return `| ${result.label} | ${result.path} | ${deviceText} | ${performanceText} | ${accessibilityText} | ${bestPracticesText} | ${seoText} | | | | |`;
|
|
131
148
|
}
|
|
132
|
-
function
|
|
149
|
+
function buildConsoleMetricsLine(result, useColor) {
|
|
150
|
+
const metrics = result.metrics;
|
|
151
|
+
const lcpText = formatMetricSeconds(metrics.lcpMs, LCP_GOOD_MS, LCP_WARN_MS, useColor);
|
|
152
|
+
const fcpText = formatMetricSeconds(metrics.fcpMs, FCP_GOOD_MS, FCP_WARN_MS, useColor);
|
|
153
|
+
const tbtText = formatMetricMilliseconds(metrics.tbtMs, TBT_GOOD_MS, TBT_WARN_MS, useColor);
|
|
154
|
+
const clsText = formatMetricRatio(metrics.cls, CLS_GOOD, CLS_WARN, useColor);
|
|
155
|
+
return `| | | | | | | | ${lcpText} | ${fcpText} | ${tbtText} | ${clsText} |`;
|
|
156
|
+
}
|
|
157
|
+
function buildConsoleErrorLine(result, useColor) {
|
|
133
158
|
const errorCode = result.runtimeErrorCode;
|
|
134
159
|
const errorMessage = result.runtimeErrorMessage;
|
|
135
160
|
if (!errorCode && !errorMessage) {
|
|
@@ -139,7 +164,7 @@ function buildConsoleRowLine2(result, useColor) {
|
|
|
139
164
|
const prefix = useColor ? `${ANSI_RED}↳ Error:${ANSI_RESET}` : "↳ Error:";
|
|
140
165
|
return ` ${prefix} ${errorText}`;
|
|
141
166
|
}
|
|
142
|
-
function
|
|
167
|
+
function buildConsoleIssuesLine(result) {
|
|
143
168
|
const issues = formatTopIssues(result.opportunities);
|
|
144
169
|
if (issues.length === 0) {
|
|
145
170
|
return "";
|
|
@@ -150,15 +175,89 @@ function formatTopIssues(opportunities) {
|
|
|
150
175
|
if (opportunities.length === 0) {
|
|
151
176
|
return "";
|
|
152
177
|
}
|
|
153
|
-
const
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
});
|
|
178
|
+
const meaningful = opportunities.filter((opp) => hasMeaningfulSavings(opp));
|
|
179
|
+
const source = meaningful.length > 0 ? meaningful : opportunities;
|
|
180
|
+
const sorted = [...source].sort(compareOpportunitiesByImpact);
|
|
181
|
+
const limit = 2;
|
|
182
|
+
const top = sorted.slice(0, limit);
|
|
183
|
+
const items = top.map((opp) => formatOpportunityLabel(opp));
|
|
160
184
|
return items.join("; ");
|
|
161
185
|
}
|
|
186
|
+
function hasMeaningfulSavings(opportunity) {
|
|
187
|
+
const savingsMs = opportunity.estimatedSavingsMs ?? 0;
|
|
188
|
+
const savingsBytes = opportunity.estimatedSavingsBytes ?? 0;
|
|
189
|
+
return savingsMs > 0 || savingsBytes > 0;
|
|
190
|
+
}
|
|
191
|
+
function compareOpportunitiesByImpact(a, b) {
|
|
192
|
+
const aMs = a.estimatedSavingsMs ?? 0;
|
|
193
|
+
const bMs = b.estimatedSavingsMs ?? 0;
|
|
194
|
+
if (aMs !== bMs) {
|
|
195
|
+
return bMs - aMs;
|
|
196
|
+
}
|
|
197
|
+
const aBytes = a.estimatedSavingsBytes ?? 0;
|
|
198
|
+
const bBytes = b.estimatedSavingsBytes ?? 0;
|
|
199
|
+
return bBytes - aBytes;
|
|
200
|
+
}
|
|
201
|
+
function formatOpportunityLabel(opportunity) {
|
|
202
|
+
const savingsMs = opportunity.estimatedSavingsMs !== undefined ? `${Math.round(opportunity.estimatedSavingsMs)}ms` : "";
|
|
203
|
+
const savingsBytes = opportunity.estimatedSavingsBytes !== undefined
|
|
204
|
+
? `${Math.round(opportunity.estimatedSavingsBytes / 1024)}KB`
|
|
205
|
+
: "";
|
|
206
|
+
const parts = [savingsMs, savingsBytes].filter((part) => part.length > 0);
|
|
207
|
+
const suffix = parts.length > 0 ? ` (${parts.join(", ")})` : "";
|
|
208
|
+
return `${opportunity.id}${suffix}`;
|
|
209
|
+
}
|
|
210
|
+
function formatMetricSeconds(valueMs, goodThresholdMs, warnThresholdMs, useColor) {
|
|
211
|
+
if (valueMs === undefined) {
|
|
212
|
+
return "-";
|
|
213
|
+
}
|
|
214
|
+
const seconds = valueMs / 1000;
|
|
215
|
+
const text = `${seconds.toFixed(1)}s`;
|
|
216
|
+
if (!useColor) {
|
|
217
|
+
return text;
|
|
218
|
+
}
|
|
219
|
+
const colour = selectColourForThreshold(valueMs, goodThresholdMs, warnThresholdMs);
|
|
220
|
+
return `${colour}${text}${ANSI_RESET}`;
|
|
221
|
+
}
|
|
222
|
+
function formatMetricMilliseconds(valueMs, goodThresholdMs, warnThresholdMs, useColor) {
|
|
223
|
+
if (valueMs === undefined) {
|
|
224
|
+
return "-";
|
|
225
|
+
}
|
|
226
|
+
const rounded = Math.round(valueMs);
|
|
227
|
+
const text = `${rounded}ms`;
|
|
228
|
+
if (!useColor) {
|
|
229
|
+
return text;
|
|
230
|
+
}
|
|
231
|
+
const colour = selectColourForThreshold(valueMs, goodThresholdMs, warnThresholdMs);
|
|
232
|
+
return `${colour}${text}${ANSI_RESET}`;
|
|
233
|
+
}
|
|
234
|
+
function formatMetricRatio(value, goodThreshold, warnThreshold, useColor) {
|
|
235
|
+
if (value === undefined) {
|
|
236
|
+
return "-";
|
|
237
|
+
}
|
|
238
|
+
const text = value.toFixed(3);
|
|
239
|
+
if (!useColor) {
|
|
240
|
+
return text;
|
|
241
|
+
}
|
|
242
|
+
const colour = selectColourForThreshold(value, goodThreshold, warnThreshold);
|
|
243
|
+
return `${colour}${text}${ANSI_RESET}`;
|
|
244
|
+
}
|
|
245
|
+
function selectColourForThreshold(value, goodThreshold, warnThreshold) {
|
|
246
|
+
if (value <= goodThreshold) {
|
|
247
|
+
return ANSI_GREEN;
|
|
248
|
+
}
|
|
249
|
+
if (value <= warnThreshold) {
|
|
250
|
+
return ANSI_YELLOW;
|
|
251
|
+
}
|
|
252
|
+
return ANSI_RED;
|
|
253
|
+
}
|
|
254
|
+
function formatDeviceLabel(device, useColor) {
|
|
255
|
+
if (!useColor) {
|
|
256
|
+
return device;
|
|
257
|
+
}
|
|
258
|
+
const colour = device === "mobile" ? ANSI_CYAN : ANSI_BLUE;
|
|
259
|
+
return `${colour}${device}${ANSI_RESET}`;
|
|
260
|
+
}
|
|
162
261
|
function colourScore(score, useColor) {
|
|
163
262
|
if (score === undefined) {
|
|
164
263
|
return "-";
|