90 lines
2.9 KiB
TypeScript
90 lines
2.9 KiB
TypeScript
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<BiomeReport | undefined> {
|
|
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<LintReport> {
|
|
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,
|
|
};
|
|
}
|