diff --git a/packages/cli/src/commands/lint.ts b/packages/cli/src/commands/lint.ts index 8d4031f4..8a0266e2 100644 --- a/packages/cli/src/commands/lint.ts +++ b/packages/cli/src/commands/lint.ts @@ -15,63 +15,86 @@ export default defineCommand({ json: { type: "boolean", description: "Output findings as JSON", default: false }, }, async run({ args }) { - const project = resolveProject(args.dir); - const htmlFiles = walkDir(project.dir).filter((f) => f.endsWith(".html")); + try { + const project = resolveProject(args.dir); + const htmlFiles = walkDir(project.dir).filter((f) => f.endsWith(".html")); - const allFindings: (HyperframeLintFinding & { file: string })[] = []; - let totalErrors = 0; - let totalWarnings = 0; + const allFindings: (HyperframeLintFinding & { file: string })[] = []; + let totalErrors = 0; + let totalWarnings = 0; - for (const file of htmlFiles) { - const html = readFileSync(join(project.dir, file), "utf-8"); - const result = lintHyperframeHtml(html, { filePath: file }); - for (const f of result.findings) { - allFindings.push({ ...f, file }); + for (const file of htmlFiles) { + const html = readFileSync(join(project.dir, file), "utf-8"); + const result = lintHyperframeHtml(html, { filePath: file }); + for (const f of result.findings) { + allFindings.push({ ...f, file }); + } + totalErrors += result.errorCount; + totalWarnings += result.warningCount; + } + + if (args.json) { + console.log( + JSON.stringify( + withMeta({ + ok: totalErrors === 0, + findings: allFindings, + errorCount: totalErrors, + warningCount: totalWarnings, + filesScanned: htmlFiles.length, + }), + null, + 2, + ), + ); + process.exit(totalErrors > 0 ? 1 : 0); } - totalErrors += result.errorCount; - totalWarnings += result.warningCount; - } - if (args.json) { console.log( - JSON.stringify( - withMeta({ - ok: totalErrors === 0, - findings: allFindings, - errorCount: totalErrors, - warningCount: totalWarnings, - filesScanned: htmlFiles.length, - }), - null, - 2, - ), + `${c.accent("◆")} Linting ${c.accent(project.name)} (${htmlFiles.length} HTML files)`, ); - process.exit(totalErrors > 0 ? 1 : 0); - } + console.log(); - console.log( - `${c.accent("◆")} Linting ${c.accent(project.name)} (${htmlFiles.length} HTML files)`, - ); - console.log(); + if (allFindings.length === 0) { + console.log(`${c.success("◇")} ${c.success("0 errors, 0 warnings")}`); + return; + } - if (allFindings.length === 0) { - console.log(`${c.success("◇")} ${c.success("0 errors, 0 warnings")}`); - return; - } + for (const finding of allFindings) { + const prefix = finding.severity === "error" ? c.error("✗") : c.warn("⚠"); + const loc = finding.elementId ? ` ${c.accent(`[${finding.elementId}]`)}` : ""; + console.log( + `${prefix} ${c.bold(finding.code)}${loc}: ${finding.message} ${c.dim(finding.file)}`, + ); + if (finding.fixHint) { + console.log(` ${c.dim(`Fix: ${finding.fixHint}`)}`); + } + } - for (const finding of allFindings) { - const prefix = finding.severity === "error" ? c.error("✗") : c.warn("⚠"); - const loc = finding.elementId ? ` ${c.accent(`[${finding.elementId}]`)}` : ""; - console.log( - `${prefix} ${c.bold(finding.code)}${loc}: ${finding.message} ${c.dim(finding.file)}`, - ); - if (finding.fixHint) { - console.log(` ${c.dim(`Fix: ${finding.fixHint}`)}`); + const summaryIcon = totalErrors > 0 ? c.error("◇") : c.success("◇"); + console.log(`\n${summaryIcon} ${totalErrors} error(s), ${totalWarnings} warning(s)`); + process.exit(totalErrors > 0 ? 1 : 0); + } catch (err: unknown) { + const message = err instanceof Error ? err.message : String(err); + if (args.json) { + console.log( + JSON.stringify( + withMeta({ + ok: false, + error: message, + findings: [], + errorCount: 0, + warningCount: 0, + filesScanned: 0, + }), + null, + 2, + ), + ); + process.exit(1); } + console.error(message); + process.exit(1); } - - const summaryIcon = totalErrors > 0 ? c.error("◇") : c.success("◇"); - console.log(`\n${summaryIcon} ${totalErrors} error(s), ${totalWarnings} warning(s)`); - process.exit(totalErrors > 0 ? 1 : 0); }, });