@svelte-vitals/core 0.22.0 → 0.23.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.
- package/dist/index.d.ts +4 -0
- package/dist/index.js +75 -21
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -629,6 +629,10 @@ interface ConsoleReportOptions {
|
|
|
629
629
|
mode?: string;
|
|
630
630
|
/** Color decorators; defaults to no color. */
|
|
631
631
|
palette?: Palette;
|
|
632
|
+
/** Show every failing/passed/route entry uncapped and ungrouped, exactly as before this option existed. Default false (capped, grouped by rule). */
|
|
633
|
+
verbose?: boolean;
|
|
634
|
+
/** Internal: set by the CLI when it has already animated the Health header itself — skips the brand/Health lines (category score lines still print). Default false. */
|
|
635
|
+
omitHeader?: boolean;
|
|
632
636
|
}
|
|
633
637
|
/**
|
|
634
638
|
* Render results as a console report string (design §7). Pure: returns a string,
|
package/dist/index.js
CHANGED
|
@@ -2399,27 +2399,53 @@ var CATEGORY_LABEL = {
|
|
|
2399
2399
|
architecture: "Architecture"
|
|
2400
2400
|
};
|
|
2401
2401
|
var CATEGORY_ORDER = ["seo", "performance", "correctness", "security", "architecture"];
|
|
2402
|
+
var MAX_RULE_GROUPS_PER_BUCKET = 5;
|
|
2403
|
+
function groupByRule(results) {
|
|
2404
|
+
const groups = /* @__PURE__ */ new Map();
|
|
2405
|
+
for (const r of results) {
|
|
2406
|
+
const bucket = groups.get(r.id);
|
|
2407
|
+
if (bucket) bucket.push(r);
|
|
2408
|
+
else groups.set(r.id, [r]);
|
|
2409
|
+
}
|
|
2410
|
+
return [...groups.entries()].map(([id, rs]) => ({ id, results: rs })).sort((a, b) => b.results.length - a.results.length || a.id.localeCompare(b.id));
|
|
2411
|
+
}
|
|
2402
2412
|
function scoreLine(p, label, { score, scoreModel }) {
|
|
2403
2413
|
const parts = [`route avg ${scoreModel.routeAverage}`];
|
|
2404
2414
|
if (scoreModel.sitePenalty > 0) parts.push(`site \u2212${scoreModel.sitePenalty}`);
|
|
2405
2415
|
if (scoreModel.criticalCap !== null) parts.push(`capped at ${scoreModel.criticalCap}: critical present`);
|
|
2406
2416
|
return `${label} Score: ${scoreColor(p, score)(`${score}/100`)} ${p.dim(`(${parts.join(" \xB7 ")})`)}`;
|
|
2407
2417
|
}
|
|
2408
|
-
|
|
2418
|
+
var MAX_ROUTES_BY_ROUTE = 10;
|
|
2419
|
+
function byRouteTree(p, results, config, verbose) {
|
|
2409
2420
|
const routes = /* @__PURE__ */ new Map();
|
|
2410
2421
|
for (const r of results) {
|
|
2411
2422
|
if (r.route === void 0) continue;
|
|
2412
2423
|
if (!routes.has(r.route)) routes.set(r.route, []);
|
|
2413
2424
|
routes.get(r.route).push(r);
|
|
2414
2425
|
}
|
|
2426
|
+
const scored = [...routes.entries()].map(([route, rs]) => ({
|
|
2427
|
+
route,
|
|
2428
|
+
rs,
|
|
2429
|
+
score: computeScore(rs, config, { applyCriticalCap: false }).score
|
|
2430
|
+
}));
|
|
2431
|
+
scored.sort((a, b) => a.score - b.score || a.route.localeCompare(b.route));
|
|
2432
|
+
const shown = verbose ? scored : scored.slice(0, MAX_ROUTES_BY_ROUTE);
|
|
2415
2433
|
const lines = [p.bold("By route"), p.dim(RULE)];
|
|
2416
|
-
for (const
|
|
2417
|
-
const { score } = computeScore(rs, config, { applyCriticalCap: false });
|
|
2434
|
+
for (const { route, rs, score } of shown) {
|
|
2418
2435
|
lines.push(`${route.padEnd(28)} ${scoreColor(p, score)(`${score}`)}`);
|
|
2419
2436
|
for (const r of rs.filter((x) => classify(x, config) === "fail")) {
|
|
2420
2437
|
lines.push(` ${p.red("\u2717")} ${r.id} ${r.message}`);
|
|
2421
2438
|
}
|
|
2422
2439
|
}
|
|
2440
|
+
if (!verbose && scored.length > MAX_ROUTES_BY_ROUTE) {
|
|
2441
|
+
const remaining = scored.slice(MAX_ROUTES_BY_ROUTE);
|
|
2442
|
+
const avgScore = Math.round(remaining.reduce((sum, r) => sum + r.score, 0) / remaining.length);
|
|
2443
|
+
lines.push(
|
|
2444
|
+
p.dim(
|
|
2445
|
+
`\u2026and ${remaining.length} more route${remaining.length > 1 ? "s" : ""} (avg score ${avgScore}) \u2014 run with --verbose to see all`
|
|
2446
|
+
)
|
|
2447
|
+
);
|
|
2448
|
+
}
|
|
2423
2449
|
lines.push("");
|
|
2424
2450
|
return lines;
|
|
2425
2451
|
}
|
|
@@ -2428,15 +2454,18 @@ function formatConsoleReport(results, config, options = {}) {
|
|
|
2428
2454
|
const summary = summarize(results, config);
|
|
2429
2455
|
const { health, categories: byCat } = computeHealth(results, config);
|
|
2430
2456
|
const present2 = CATEGORY_ORDER.filter((c) => byCat[c] !== void 0);
|
|
2431
|
-
const
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2457
|
+
const lines = [];
|
|
2458
|
+
if (!options.omitHeader) {
|
|
2459
|
+
lines.push(
|
|
2460
|
+
p.bold(`Svelte Vitals \xB7 ${options.mode ?? "static mode"}`),
|
|
2461
|
+
"",
|
|
2462
|
+
`${p.bold("Health:")} ${scoreColor(p, health)(`${health}/100`)}`
|
|
2463
|
+
);
|
|
2464
|
+
}
|
|
2436
2465
|
for (const c of present2) {
|
|
2437
|
-
|
|
2466
|
+
lines.push(scoreLine(p, CATEGORY_LABEL[c] ?? c, byCat[c]));
|
|
2438
2467
|
}
|
|
2439
|
-
|
|
2468
|
+
lines.push("");
|
|
2440
2469
|
const SEVERITY_COLOR = {
|
|
2441
2470
|
critical: (s) => p.red(p.bold(s)),
|
|
2442
2471
|
warning: (s) => p.yellow(p.bold(s)),
|
|
@@ -2447,24 +2476,46 @@ function formatConsoleReport(results, config, options = {}) {
|
|
|
2447
2476
|
const bucket = failures.filter((r) => effectiveSeverity(r, config) === severity);
|
|
2448
2477
|
if (bucket.length === 0) continue;
|
|
2449
2478
|
lines.push(SEVERITY_COLOR[severity](`${SEVERITY_TITLE[severity]} (${bucket.length})`), p.dim(RULE));
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
|
|
2479
|
+
if (options.verbose) {
|
|
2480
|
+
for (const r of bucket) {
|
|
2481
|
+
lines.push(`${p.red("\u2717")} ${r.id} ${r.message}`);
|
|
2482
|
+
if (r.route) lines.push(p.dim(` ${r.route}`));
|
|
2483
|
+
if (r.location) lines.push(p.dim(` ${r.location}${r.line ? `:${r.line}` : ""}`));
|
|
2484
|
+
}
|
|
2485
|
+
} else {
|
|
2486
|
+
const groups = groupByRule(bucket);
|
|
2487
|
+
const shownGroups = groups.slice(0, MAX_RULE_GROUPS_PER_BUCKET);
|
|
2488
|
+
for (const group of shownGroups) {
|
|
2489
|
+
const r = group.results[0];
|
|
2490
|
+
lines.push(`${p.red("\u2717")} ${r.id} ${r.message}`);
|
|
2491
|
+
if (r.route) lines.push(p.dim(` ${r.route}`));
|
|
2492
|
+
if (r.location) lines.push(p.dim(` ${r.location}${r.line ? `:${r.line}` : ""}`));
|
|
2493
|
+
if (group.results.length > 1) {
|
|
2494
|
+
lines.push(p.dim(` \u2026and ${group.results.length - 1} more`));
|
|
2495
|
+
}
|
|
2496
|
+
}
|
|
2497
|
+
if (groups.length > MAX_RULE_GROUPS_PER_BUCKET) {
|
|
2498
|
+
const remaining = groups.length - MAX_RULE_GROUPS_PER_BUCKET;
|
|
2499
|
+
lines.push(
|
|
2500
|
+
p.dim(`\u2026and ${remaining} more rule${remaining > 1 ? "s" : ""} affected \u2014 run with --verbose to see all`)
|
|
2501
|
+
);
|
|
2502
|
+
}
|
|
2454
2503
|
}
|
|
2455
2504
|
lines.push("");
|
|
2456
2505
|
}
|
|
2457
2506
|
const passed = results.filter((r) => classify(r, config) !== "fail");
|
|
2458
2507
|
if (passed.length > 0) {
|
|
2459
2508
|
lines.push(p.bold(`Passed (${passed.length})`), p.dim(RULE));
|
|
2460
|
-
|
|
2461
|
-
const
|
|
2462
|
-
|
|
2463
|
-
|
|
2509
|
+
if (options.verbose) {
|
|
2510
|
+
for (const r of passed) {
|
|
2511
|
+
const marker = classify(r, config) === "dynamic" ? p.cyan(" \u21AF dynamic") : "";
|
|
2512
|
+
const route = r.route ? ` ${r.route}` : "";
|
|
2513
|
+
lines.push(`${p.green("\u2713")} ${r.id} ${r.message}${marker}${route}`);
|
|
2514
|
+
}
|
|
2464
2515
|
}
|
|
2465
2516
|
lines.push("");
|
|
2466
2517
|
}
|
|
2467
|
-
if (options.byRoute) lines.push(...byRouteTree(p, results, config));
|
|
2518
|
+
if (options.byRoute) lines.push(...byRouteTree(p, results, config, options.verbose ?? false));
|
|
2468
2519
|
if (summary.dynamic > 0) lines.push(p.dim("\u21AF = set dynamically (verified at runtime)."));
|
|
2469
2520
|
return lines.join("\n").replace(/\n+$/, "\n");
|
|
2470
2521
|
}
|
|
@@ -2665,6 +2716,9 @@ function locationOf(issue, route) {
|
|
|
2665
2716
|
if (issue.location) return issue.line !== void 0 ? `${issue.location}:${issue.line}` : issue.location;
|
|
2666
2717
|
return route ?? "-";
|
|
2667
2718
|
}
|
|
2719
|
+
function messageWithRecommendation(issue) {
|
|
2720
|
+
return issue.recommendation ? `${issue.title} ${issue.recommendation}` : issue.title;
|
|
2721
|
+
}
|
|
2668
2722
|
function flattenFindings(report) {
|
|
2669
2723
|
const findings = [];
|
|
2670
2724
|
for (const r of report.routes) {
|
|
@@ -2673,7 +2727,7 @@ function flattenFindings(report) {
|
|
|
2673
2727
|
severity: issue.severity,
|
|
2674
2728
|
id: issue.id,
|
|
2675
2729
|
location: locationOf(issue, r.route),
|
|
2676
|
-
message: issue
|
|
2730
|
+
message: messageWithRecommendation(issue)
|
|
2677
2731
|
});
|
|
2678
2732
|
}
|
|
2679
2733
|
}
|
|
@@ -2682,7 +2736,7 @@ function flattenFindings(report) {
|
|
|
2682
2736
|
severity: issue.severity,
|
|
2683
2737
|
id: issue.id,
|
|
2684
2738
|
location: locationOf(issue, void 0),
|
|
2685
|
-
message: issue
|
|
2739
|
+
message: messageWithRecommendation(issue)
|
|
2686
2740
|
});
|
|
2687
2741
|
}
|
|
2688
2742
|
return findings.map((f, index) => ({ f, index })).sort((a, b) => SEVERITY_RANK2[a.f.severity] - SEVERITY_RANK2[b.f.severity] || a.index - b.index).map(({ f }) => f);
|