From 5b0b15da2b834818e5f6d2ccdade80aed5777046 Mon Sep 17 00:00:00 2001 From: Jacob Bolda Date: Sun, 26 Jul 2026 23:15:53 -0500 Subject: [PATCH] pass exit code on lint command --- .changeset/petite-ghosts-jump.md | 5 ++++ src/commands/lint.ts | 39 +++++++++++++++++++++++--------- 2 files changed, 33 insertions(+), 11 deletions(-) create mode 100644 .changeset/petite-ghosts-jump.md diff --git a/.changeset/petite-ghosts-jump.md b/.changeset/petite-ghosts-jump.md new file mode 100644 index 0000000..39b506a --- /dev/null +++ b/.changeset/petite-ghosts-jump.md @@ -0,0 +1,5 @@ +--- +"@bomb.sh/tools": patch +--- + +`lint` CLI command respects internal `lint` call and passes the exit code to the user. diff --git a/src/commands/lint.ts b/src/commands/lint.ts index 374af78..95f5242 100644 --- a/src/commands/lint.ts +++ b/src/commands/lint.ts @@ -5,7 +5,7 @@ import { x } from 'tinyexec'; import type { JSONReport as KnipJSONReport } from 'knip'; import type { CommandContext } from '../context.ts'; import { getPublicSurface } from '../surface.ts'; -import { local } from '../utils.ts'; +import { local, ToolsError } from '../utils.ts'; const oxlintConfig = fileURLToPath(new URL('../../oxlintrc.json', import.meta.url)); @@ -100,8 +100,9 @@ export async function runOxlint(targets: string[], fix?: boolean): Promise { throwOnError: false, }); const output = result.stdout + result.stderr; - if (!output.trim()) return []; + if (!output.trim()) { + if (result.exitCode !== 0) + throw new ToolsError(`tsgo exited with code ${result.exitCode}`, 'tsgo-exit'); + return []; + } const violations: Violation[] = []; const re = /^(.+)\((\d+),(\d+)\): (error|warning) (TS\d+): (.+)$/gm; @@ -318,7 +330,7 @@ export function printJson(violations: Violation[]) { async function collectViolations( targets: string[], options?: { strict?: boolean }, -): Promise { +): Promise<{ violations: Violation[]; failed: boolean }> { // oxlint honors targets (default: project-wide); tsgo runs in project // mode unless the user explicitly narrowed the target set. const explicit = targets.length > 0; @@ -329,14 +341,16 @@ async function collectViolations( ]); const violations: Violation[] = []; + let failed = false; for (const result of results) { if (result.status === 'fulfilled') { violations.push(...result.value); } else { + failed = true; console.error(result.reason); } } - return violations; + return { violations, failed }; } export async function lint(ctx: CommandContext) { @@ -354,20 +368,23 @@ export async function lint(ctx: CommandContext) { await runKnipFix(args.strict); // Report remaining - const remaining = await collectViolations(targets, { strict: args.strict }); + const { violations: remaining, failed } = await collectViolations(targets, { + strict: args.strict, + }); if (remaining.length > 0) { print(remaining); - if (remaining.some((v) => v.level === 'error')) process.exit(1); + if (remaining.some((v) => v.level === 'error') || failed) process.exit(1); return; } + if (failed) process.exit(1); if (!json) console.log('No issues found.'); return; } // Default: report only - const violations = await collectViolations(targets, { strict: args.strict }); + const { violations, failed } = await collectViolations(targets, { strict: args.strict }); print(violations); - if (violations.some((v) => v.level === 'error')) { + if (violations.some((v) => v.level === 'error') || failed) { process.exit(1); } }