import { nodeCommandRunner, probeBiomeAvailable, runBiome, } from "../biome-runner/biome-runner.js"; import { LinterError } from "../error-codes/error-codes.js"; import { nodeGlobAdapter } from "../glob-resolver/glob-resolver.js"; import { nodeFsAdapter, processMarkdownFiles, } from "../markdown-fixer/markdown-fixer.js"; import { recordRunDuration } from "../telemetry/telemetry.js"; import type { BiomeReport, ClockLike, LintReport, MarkdownFixReport, UnifiedLintOptions, } from "../types/linter.types.js"; const defaultClock: ClockLike = { now: () => Date.now() }; async function runBiomeLane( options: UnifiedLintOptions ): Promise { const runner = options.runner ?? nodeCommandRunner; const cwd = options.cwd ?? "."; const available = await probeBiomeAvailable({ runner, cwd }); if (!available) { return undefined; } const mode = options.fix === true ? "check-write" : "check"; try { return await runBiome({ cwd, mode, runner }); } catch (err) { // `runBiome` only raises `BIOME_FAILED` in `check` mode AND always // attaches the parsed `BiomeReport` under `details.report`, so this // branch is the documented surface that surfaces the embedded // report instead of re-throwing. All other errors propagate. if (err instanceof LinterError && err.code === "LINTER.BIOME_FAILED") { return err.details["report"] as BiomeReport; } throw err; } } async function runMarkdownLane( options: UnifiedLintOptions, clock: ClockLike ): Promise<{ reports: MarkdownFixReport[]; durationMs: number; }> { const start = clock.now(); const reports = await processMarkdownFiles({ cwd: options.cwd ?? ".", fs: options.fs ?? nodeFsAdapter, glob: options.glob ?? nodeGlobAdapter, ...(options.patterns ? { patterns: options.patterns } : {}), }); const durationMs = Math.max(0, clock.now() - start); recordRunDuration("markdown", durationMs); return { reports, durationMs }; } /** * Orchestrator. Runs the biome lane (if available) and the markdown lane, * aggregates them into a {@link LintReport}, and returns. NEVER calls * `process.exit` — translation to an exit code is the CLI's job. */ export async function runUnifiedLint( options: UnifiedLintOptions = {} ): Promise { const clock = options.clock ?? defaultClock; const start = clock.now(); const biome = options.markdownOnly === true ? undefined : await runBiomeLane(options); const { reports } = await runMarkdownLane(options, clock); const durationMs = Math.max(0, clock.now() - start); const biomeSuccess = biome?.success ?? true; const success = biomeSuccess; recordRunDuration("total", durationMs); return { biome, markdown: reports, success, durationMs, }; }