[READ-ONLY] Mirror of https://github.com/bombshell-dev/tools. Internal CLI to standardize tooling across all Bombshell projects
0

Configure Feed

Select the types of activity you want to include in your feed.

[ci] format

authored by

Nate Moore and committed by
bombshell-bot
(Mar 16, 2026, 1:58 AM UTC) d3d25325 825edd27

+218 -218
+13 -13
src/commands/build.ts
··· 3 3 import { local } from "../utils.ts"; 4 4 5 5 export async function build(ctx: CommandContext) { 6 - const stdio = x(local("tsdown"), [ 7 - "src/bin.ts", 8 - "--format", 9 - "esm", 10 - "--sourcemap", 11 - "--clean", 12 - "--unbundle", 13 - "--no-config", 14 - ...ctx.args, 15 - ]); 6 + const stdio = x(local("tsdown"), [ 7 + "src/bin.ts", 8 + "--format", 9 + "esm", 10 + "--sourcemap", 11 + "--clean", 12 + "--unbundle", 13 + "--no-config", 14 + ...ctx.args, 15 + ]); 16 16 17 - for await (const line of stdio) { 18 - console.log(line); 19 - } 17 + for await (const line of stdio) { 18 + console.log(line); 19 + } 20 20 }
+204 -204
src/commands/lint.ts
··· 10 10 // -- Types -- 11 11 12 12 interface Violation { 13 - tool: "oxlint" | "publint" | "knip" | "tsc"; 14 - level: "error" | "warning" | "suggestion"; 15 - code: string; 16 - message: string; 17 - file?: string; 18 - line?: number; 19 - column?: number; 13 + tool: "oxlint" | "publint" | "knip" | "tsc"; 14 + level: "error" | "warning" | "suggestion"; 15 + code: string; 16 + message: string; 17 + file?: string; 18 + line?: number; 19 + column?: number; 20 20 } 21 21 22 22 // -- Tool Runners -- 23 23 24 24 async function runOxlint(targets: string[], fix?: boolean): Promise<Violation[]> { 25 - const args = ["-c", oxlintConfig, "--format=json", ...targets]; 26 - if (fix) args.push("--fix"); 27 - const result = await x(local("oxlint"), args, { throwOnError: false }); 28 - const json = JSON.parse(result.stdout); 29 - return (json.diagnostics ?? []).map( 30 - (d: { 31 - message: string; 32 - code: string; 33 - severity: string; 34 - filename?: string; 35 - labels?: Array<{ span?: { line?: number; column?: number } }>; 36 - }) => ({ 37 - tool: "oxlint" as const, 38 - level: d.severity === "error" ? "error" : "warning", 39 - code: d.code ?? "unknown", 40 - message: d.message, 41 - file: d.filename, 42 - line: d.labels?.[0]?.span?.line, 43 - column: d.labels?.[0]?.span?.column, 44 - }), 45 - ); 25 + const args = ["-c", oxlintConfig, "--format=json", ...targets]; 26 + if (fix) args.push("--fix"); 27 + const result = await x(local("oxlint"), args, { throwOnError: false }); 28 + const json = JSON.parse(result.stdout); 29 + return (json.diagnostics ?? []).map( 30 + (d: { 31 + message: string; 32 + code: string; 33 + severity: string; 34 + filename?: string; 35 + labels?: Array<{ span?: { line?: number; column?: number } }>; 36 + }) => ({ 37 + tool: "oxlint" as const, 38 + level: d.severity === "error" ? "error" : "warning", 39 + code: d.code ?? "unknown", 40 + message: d.message, 41 + file: d.filename, 42 + line: d.labels?.[0]?.span?.line, 43 + column: d.labels?.[0]?.span?.column, 44 + }), 45 + ); 46 46 } 47 47 48 48 async function runPublint(): Promise<Violation[]> { 49 - const result = await publint({ strict: true }); 50 - return result.messages.map((m) => ({ 51 - tool: "publint" as const, 52 - level: m.type === "error" ? "error" : m.type === "warning" ? "warning" : "suggestion", 53 - code: m.code, 54 - message: m.code, 55 - file: "package.json", 56 - line: undefined, 57 - column: undefined, 58 - })); 49 + const result = await publint({ strict: true }); 50 + return result.messages.map((m) => ({ 51 + tool: "publint" as const, 52 + level: m.type === "error" ? "error" : m.type === "warning" ? "warning" : "suggestion", 53 + code: m.code, 54 + message: m.code, 55 + file: "package.json", 56 + line: undefined, 57 + column: undefined, 58 + })); 59 59 } 60 60 61 61 interface KnipIssue { 62 - file: string; 63 - dependencies: Array<{ name: string; line: number; col: number }>; 64 - devDependencies: Array<{ name: string; line: number; col: number }>; 65 - exports: Array<{ name: string; line: number; col: number }>; 66 - types: Array<{ name: string; line: number; col: number }>; 62 + file: string; 63 + dependencies: Array<{ name: string; line: number; col: number }>; 64 + devDependencies: Array<{ name: string; line: number; col: number }>; 65 + exports: Array<{ name: string; line: number; col: number }>; 66 + types: Array<{ name: string; line: number; col: number }>; 67 67 } 68 68 69 69 async function runKnip(): Promise<Violation[]> { 70 - const args = ["--no-progress", "--reporter", "json"]; 71 - const result = await x(local("knip"), args, { throwOnError: false }); 72 - if (!result.stdout.trim()) return []; 73 - const json = JSON.parse(result.stdout); 74 - const violations: Violation[] = []; 70 + const args = ["--no-progress", "--reporter", "json"]; 71 + const result = await x(local("knip"), args, { throwOnError: false }); 72 + if (!result.stdout.trim()) return []; 73 + const json = JSON.parse(result.stdout); 74 + const violations: Violation[] = []; 75 75 76 - for (const issue of json.issues as KnipIssue[]) { 77 - for (const dep of issue.dependencies) { 78 - violations.push({ 79 - tool: "knip", 80 - level: "warning", 81 - code: "unused-dependency", 82 - message: `Unused dependency '${dep.name}'`, 83 - file: issue.file, 84 - line: dep.line, 85 - column: dep.col, 86 - }); 87 - } 88 - for (const dep of issue.devDependencies) { 89 - violations.push({ 90 - tool: "knip", 91 - level: "warning", 92 - code: "unused-devDependency", 93 - message: `Unused devDependency '${dep.name}'`, 94 - file: issue.file, 95 - line: dep.line, 96 - column: dep.col, 97 - }); 98 - } 99 - for (const exp of issue.exports) { 100 - violations.push({ 101 - tool: "knip", 102 - level: "warning", 103 - code: "unused-export", 104 - message: `Unused export '${exp.name}'`, 105 - file: issue.file, 106 - line: exp.line, 107 - column: exp.col, 108 - }); 109 - } 110 - for (const t of issue.types) { 111 - violations.push({ 112 - tool: "knip", 113 - level: "warning", 114 - code: "unused-type", 115 - message: `Unused type '${t.name}'`, 116 - file: issue.file, 117 - line: t.line, 118 - column: t.col, 119 - }); 120 - } 121 - } 76 + for (const issue of json.issues as KnipIssue[]) { 77 + for (const dep of issue.dependencies) { 78 + violations.push({ 79 + tool: "knip", 80 + level: "warning", 81 + code: "unused-dependency", 82 + message: `Unused dependency '${dep.name}'`, 83 + file: issue.file, 84 + line: dep.line, 85 + column: dep.col, 86 + }); 87 + } 88 + for (const dep of issue.devDependencies) { 89 + violations.push({ 90 + tool: "knip", 91 + level: "warning", 92 + code: "unused-devDependency", 93 + message: `Unused devDependency '${dep.name}'`, 94 + file: issue.file, 95 + line: dep.line, 96 + column: dep.col, 97 + }); 98 + } 99 + for (const exp of issue.exports) { 100 + violations.push({ 101 + tool: "knip", 102 + level: "warning", 103 + code: "unused-export", 104 + message: `Unused export '${exp.name}'`, 105 + file: issue.file, 106 + line: exp.line, 107 + column: exp.col, 108 + }); 109 + } 110 + for (const t of issue.types) { 111 + violations.push({ 112 + tool: "knip", 113 + level: "warning", 114 + code: "unused-type", 115 + message: `Unused type '${t.name}'`, 116 + file: issue.file, 117 + line: t.line, 118 + column: t.col, 119 + }); 120 + } 121 + } 122 122 123 - for (const file of json.files as string[]) { 124 - violations.push({ 125 - tool: "knip", 126 - level: "warning", 127 - code: "unused-file", 128 - message: `Unused file`, 129 - file, 130 - }); 131 - } 123 + for (const file of json.files as string[]) { 124 + violations.push({ 125 + tool: "knip", 126 + level: "warning", 127 + code: "unused-file", 128 + message: `Unused file`, 129 + file, 130 + }); 131 + } 132 132 133 - return violations; 133 + return violations; 134 134 } 135 135 136 136 async function runTypeScript(targets: string[]): Promise<Violation[]> { 137 - const args = 138 - targets.length > 0 139 - ? ["--noEmit", "--pretty", "false", ...targets] 140 - : ["--noEmit", "--pretty", "false"]; 141 - const result = await x(local("tsgo"), args, { throwOnError: false }); 142 - const output = result.stdout + result.stderr; 143 - if (!output.trim()) return []; 137 + const args = 138 + targets.length > 0 139 + ? ["--noEmit", "--pretty", "false", ...targets] 140 + : ["--noEmit", "--pretty", "false"]; 141 + const result = await x(local("tsgo"), args, { throwOnError: false }); 142 + const output = result.stdout + result.stderr; 143 + if (!output.trim()) return []; 144 144 145 - const violations: Violation[] = []; 146 - const re = /^(.+)\((\d+),(\d+)\): (error|warning) (TS\d+): (.+)$/gm; 147 - let match: RegExpExecArray | null; 148 - while ((match = re.exec(output)) !== null) { 149 - violations.push({ 150 - tool: "tsc", 151 - level: match[4] === "error" ? "error" : "warning", 152 - code: match[5]!, 153 - message: match[6]!, 154 - file: match[1]!, 155 - line: Number(match[2]), 156 - column: Number(match[3]), 157 - }); 158 - } 159 - return violations; 145 + const violations: Violation[] = []; 146 + const re = /^(.+)\((\d+),(\d+)\): (error|warning) (TS\d+): (.+)$/gm; 147 + let match: RegExpExecArray | null; 148 + while ((match = re.exec(output)) !== null) { 149 + violations.push({ 150 + tool: "tsc", 151 + level: match[4] === "error" ? "error" : "warning", 152 + code: match[5]!, 153 + message: match[6]!, 154 + file: match[1]!, 155 + line: Number(match[2]), 156 + column: Number(match[3]), 157 + }); 158 + } 159 + return violations; 160 160 } 161 161 162 162 // -- Output -- 163 163 164 164 function printViolations(violations: Violation[]) { 165 - const grouped = new Map<string, Violation[]>(); 166 - for (const v of violations) { 167 - const key = v.file ?? "(project)"; 168 - if (!grouped.has(key)) grouped.set(key, []); 169 - grouped.get(key)!.push(v); 170 - } 165 + const grouped = new Map<string, Violation[]>(); 166 + for (const v of violations) { 167 + const key = v.file ?? "(project)"; 168 + if (!grouped.has(key)) grouped.set(key, []); 169 + grouped.get(key)!.push(v); 170 + } 171 171 172 - const colors = { 173 - error: "\x1b[31m", 174 - warning: "\x1b[33m", 175 - suggestion: "\x1b[34m", 176 - dim: "\x1b[2m", 177 - reset: "\x1b[0m", 178 - }; 172 + const colors = { 173 + error: "\x1b[31m", 174 + warning: "\x1b[33m", 175 + suggestion: "\x1b[34m", 176 + dim: "\x1b[2m", 177 + reset: "\x1b[0m", 178 + }; 179 179 180 - for (const [file, items] of grouped) { 181 - console.log(`\n${file}`); 182 - for (const v of items) { 183 - const loc = v.line != null ? ` ${v.line}:${v.column ?? 0}` : " -"; 184 - const color = colors[v.level]; 185 - const tag = `${v.tool}/${v.code}`; 186 - console.log( 187 - `${colors.dim}${loc.padEnd(10)}${colors.reset}${color}${v.level.padEnd(12)}${colors.reset}${v.message} ${colors.dim}${tag}${colors.reset}`, 188 - ); 189 - } 190 - } 180 + for (const [file, items] of grouped) { 181 + console.log(`\n${file}`); 182 + for (const v of items) { 183 + const loc = v.line != null ? ` ${v.line}:${v.column ?? 0}` : " -"; 184 + const color = colors[v.level]; 185 + const tag = `${v.tool}/${v.code}`; 186 + console.log( 187 + `${colors.dim}${loc.padEnd(10)}${colors.reset}${color}${v.level.padEnd(12)}${colors.reset}${v.message} ${colors.dim}${tag}${colors.reset}`, 188 + ); 189 + } 190 + } 191 191 192 - const counts = { error: 0, warning: 0, suggestion: 0 }; 193 - for (const v of violations) counts[v.level]++; 194 - const parts = []; 195 - if (counts.error) 196 - parts.push(`${colors.error}${counts.error} error${counts.error > 1 ? "s" : ""}${colors.reset}`); 197 - if (counts.warning) 198 - parts.push( 199 - `${colors.warning}${counts.warning} warning${counts.warning > 1 ? "s" : ""}${colors.reset}`, 200 - ); 201 - if (counts.suggestion) 202 - parts.push( 203 - `${colors.suggestion}${counts.suggestion} suggestion${counts.suggestion > 1 ? "s" : ""}${colors.reset}`, 204 - ); 205 - if (parts.length > 0) { 206 - console.log(`\n${parts.join(", ")}`); 207 - } else { 208 - console.log("\nNo issues found."); 209 - } 192 + const counts = { error: 0, warning: 0, suggestion: 0 }; 193 + for (const v of violations) counts[v.level]++; 194 + const parts = []; 195 + if (counts.error) 196 + parts.push(`${colors.error}${counts.error} error${counts.error > 1 ? "s" : ""}${colors.reset}`); 197 + if (counts.warning) 198 + parts.push( 199 + `${colors.warning}${counts.warning} warning${counts.warning > 1 ? "s" : ""}${colors.reset}`, 200 + ); 201 + if (counts.suggestion) 202 + parts.push( 203 + `${colors.suggestion}${counts.suggestion} suggestion${counts.suggestion > 1 ? "s" : ""}${colors.reset}`, 204 + ); 205 + if (parts.length > 0) { 206 + console.log(`\n${parts.join(", ")}`); 207 + } else { 208 + console.log("\nNo issues found."); 209 + } 210 210 } 211 211 212 212 // -- Main -- 213 213 214 214 async function collectViolations(targets: string[]): Promise<Violation[]> { 215 - const results = await Promise.allSettled([ 216 - runOxlint(targets), 217 - runPublint(), 218 - runKnip(), 219 - runTypeScript(targets), 220 - ]); 215 + const results = await Promise.allSettled([ 216 + runOxlint(targets), 217 + runPublint(), 218 + runKnip(), 219 + runTypeScript(targets), 220 + ]); 221 221 222 - const violations: Violation[] = []; 223 - for (const result of results) { 224 - if (result.status === "fulfilled") { 225 - violations.push(...result.value); 226 - } else { 227 - console.error(result.reason); 228 - } 229 - } 230 - return violations; 222 + const violations: Violation[] = []; 223 + for (const result of results) { 224 + if (result.status === "fulfilled") { 225 + violations.push(...result.value); 226 + } else { 227 + console.error(result.reason); 228 + } 229 + } 230 + return violations; 231 231 } 232 232 233 233 export async function lint(ctx: CommandContext) { 234 - const args = parse(ctx.args, { 235 - boolean: ["fix"], 236 - }); 237 - const targets = args._.length > 0 ? args._.map(String) : ["./src"]; 234 + const args = parse(ctx.args, { 235 + boolean: ["fix"], 236 + }); 237 + const targets = args._.length > 0 ? args._.map(String) : ["./src"]; 238 238 239 - if (args.fix) { 240 - await runOxlint(targets, true); 239 + if (args.fix) { 240 + await runOxlint(targets, true); 241 241 242 - // Report remaining 243 - const remaining = await collectViolations(targets); 244 - if (remaining.length > 0) { 245 - printViolations(remaining); 246 - process.exit(1); 247 - } 248 - console.log("No issues found."); 249 - return; 250 - } 242 + // Report remaining 243 + const remaining = await collectViolations(targets); 244 + if (remaining.length > 0) { 245 + printViolations(remaining); 246 + process.exit(1); 247 + } 248 + console.log("No issues found."); 249 + return; 250 + } 251 251 252 - // Default: report only 253 - const violations = await collectViolations(targets); 254 - printViolations(violations); 255 - if (violations.some((v) => v.level === "error")) { 256 - process.exit(1); 257 - } 252 + // Default: report only 253 + const violations = await collectViolations(targets); 254 + printViolations(violations); 255 + if (violations.some((v) => v.level === "error")) { 256 + process.exit(1); 257 + } 258 258 }
+1 -1
src/utils.ts
··· 1 1 import { fileURLToPath } from "node:url"; 2 2 3 3 export function local(file: string) { 4 - return fileURLToPath(new URL(`../node_modules/.bin/${file}`, import.meta.url)); 4 + return fileURLToPath(new URL(`../node_modules/.bin/${file}`, import.meta.url)); 5 5 }