fix(cli): distinguish info vs warning severity in lint output

The lint command displayed info-level findings with the warning icon
and counted them in the warning total, while JSON correctly reported
severity as "info". Now info findings show a distinct icon (ℹ) and
are counted separately in both human and JSON output.

Reproducer:
  # Create a composition with a timed element missing class="clip"
  npx hyperframes lint        # Was: "1 warning(s)" with ⚠ icon
  npx hyperframes lint --json # Was: severity: "info" but warningCount: 1
  # Human and JSON output now agree
This commit is contained in:
Miguel Ángel
2026-03-30 18:29:47 +02:00
parent 8bc99ebd9d
commit 5f731892f1
2 changed files with 27 additions and 5 deletions
+15 -2
View File
@@ -22,6 +22,7 @@ export default defineCommand({
const allFindings: (HyperframeLintFinding & { file: string })[] = [];
let totalErrors = 0;
let totalWarnings = 0;
let totalInfos = 0;
for (const file of htmlFiles) {
const html = readFileSync(join(project.dir, file), "utf-8");
@@ -31,6 +32,7 @@ export default defineCommand({
}
totalErrors += result.errorCount;
totalWarnings += result.warningCount;
totalInfos += result.infoCount;
}
if (args.json) {
@@ -41,6 +43,7 @@ export default defineCommand({
findings: allFindings,
errorCount: totalErrors,
warningCount: totalWarnings,
infoCount: totalInfos,
filesScanned: htmlFiles.length,
}),
null,
@@ -61,7 +64,12 @@ export default defineCommand({
}
for (const finding of allFindings) {
const prefix = finding.severity === "error" ? c.error("✗") : c.warn("⚠");
const prefix =
finding.severity === "error"
? c.error("✗")
: finding.severity === "warning"
? c.warn("⚠")
: c.dim("");
const loc = finding.elementId ? ` ${c.accent(`[${finding.elementId}]`)}` : "";
console.log(
`${prefix} ${c.bold(finding.code)}${loc}: ${finding.message} ${c.dim(finding.file)}`,
@@ -72,7 +80,11 @@ export default defineCommand({
}
const summaryIcon = totalErrors > 0 ? c.error("◇") : c.success("◇");
console.log(`\n${summaryIcon} ${totalErrors} error(s), ${totalWarnings} warning(s)`);
const summaryParts = [`${totalErrors} error(s)`, `${totalWarnings} warning(s)`];
if (totalInfos > 0) {
summaryParts.push(`${totalInfos} info(s)`);
}
console.log(`\n${summaryIcon} ${summaryParts.join(", ")}`);
process.exit(totalErrors > 0 ? 1 : 0);
} catch (err: unknown) {
const message = err instanceof Error ? err.message : String(err);
@@ -85,6 +97,7 @@ export default defineCommand({
findings: [],
errorCount: 0,
warningCount: 0,
infoCount: 0,
filesScanned: 0,
}),
null,
@@ -3,11 +3,20 @@ import path from "node:path";
import { lintHyperframeHtml } from "../src/lint/hyperframeLinter";
import type { HyperframeLintResult } from "../src/lint/types";
function formatCounts(result: HyperframeLintResult): string {
const parts = [`${result.warningCount} warning${result.warningCount === 1 ? "" : "s"}`];
if (result.infoCount > 0) {
parts.push(`${result.infoCount} info${result.infoCount === 1 ? "" : "s"}`);
}
return parts.join(", ");
}
function formatHumanOutput(result: HyperframeLintResult, resolvedPath: string): string {
const counts = result.ok
? formatCounts(result)
: `${result.errorCount} error${result.errorCount === 1 ? "" : "s"}, ${formatCounts(result)}`;
const lines = [
result.ok
? `PASS ${resolvedPath} (${result.warningCount} warning${result.warningCount === 1 ? "" : "s"})`
: `FAIL ${resolvedPath} (${result.errorCount} error${result.errorCount === 1 ? "" : "s"}, ${result.warningCount} warning${result.warningCount === 1 ? "" : "s"})`,
result.ok ? `PASS ${resolvedPath} (${counts})` : `FAIL ${resolvedPath} (${counts})`,
];
for (const finding of result.findings) {