first commit
This commit is contained in:
117
docs/devtools/tooling/linter/src/biome-runner/biome-runner.md
Normal file
117
docs/devtools/tooling/linter/src/biome-runner/biome-runner.md
Normal file
@@ -0,0 +1,117 @@
|
||||
# `biome-runner`
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
`@suaveplan/linter/biome-runner` is the seam between the linter and the Biome CLI. Rather
|
||||
than calling `execSync` from library code (which makes tests impossible and blocks the
|
||||
event loop), the runner shells out through an injectable `CommandRunner` whose default
|
||||
implementation wraps `node:child_process.spawn`. Tests pass real fake runners that
|
||||
return canned `CommandResult` objects; production callers get the real subprocess. Every
|
||||
run is wrapped in duration measurement so the OTel histogram captures the cost of every
|
||||
biome invocation across the project.
|
||||
|
||||
## Features
|
||||
|
||||
- Three modes — `"check"` (read-only), `"check-write"` (lint+fix, `--write --unsafe`),
|
||||
`"format-write"` (formatter only with `--write`).
|
||||
- Returns a `BiomeReport` with `mode`, `exitCode`, `stdout`, `stderr`, `success`, and
|
||||
`durationMs` so downstream callers can render summaries without re-parsing.
|
||||
- `"check"` mode throws `LinterError(LINTER.BIOME_FAILED)` on a non-zero exit (the
|
||||
expected contract for CI gates); `"check-write"` and `"format-write"` return the
|
||||
report unconditionally so write-mode runs can report success even when biome surfaced
|
||||
diagnostics that it auto-fixed.
|
||||
- `probeBiomeAvailable({ runner })` — non-fatal `bunx biome --version` ping returning
|
||||
`boolean`. Used by the orchestrator to gate the biome lane.
|
||||
- Zod-validated options — invalid `mode` values raise
|
||||
`LinterError(LINTER.INVALID_OPTIONS)`.
|
||||
- Injectable `ClockLike` for deterministic duration measurement in tests.
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import {
|
||||
nodeCommandRunner,
|
||||
runBiome,
|
||||
} from "@suaveplan/linter/biome-runner";
|
||||
|
||||
const report = await runBiome({
|
||||
cwd: ".",
|
||||
mode: "check",
|
||||
runner: nodeCommandRunner,
|
||||
});
|
||||
console.log(report.success, report.durationMs);
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
Drive the runner from a test with a recording fake runner:
|
||||
|
||||
```ts
|
||||
import { runBiome } from "@suaveplan/linter/biome-runner";
|
||||
import type { CommandInput, CommandRunner } from "@suaveplan/linter";
|
||||
|
||||
const calls: CommandInput[] = [];
|
||||
const runner: CommandRunner = {
|
||||
async run(input) {
|
||||
calls.push(input);
|
||||
return { exitCode: 0, stdout: "ok", stderr: "" };
|
||||
},
|
||||
};
|
||||
const clock = { now: () => 1000 };
|
||||
|
||||
const report = await runBiome({
|
||||
cwd: ".",
|
||||
mode: "check-write",
|
||||
runner,
|
||||
clock,
|
||||
});
|
||||
// calls[0].args === ['biome', 'check', '--write', '--unsafe', '--max-diagnostics', '500', '.']
|
||||
// report.success === true
|
||||
// report.durationMs === 0
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `runBiome(options): Promise<BiomeReport>`
|
||||
|
||||
- `options.cwd: string` — required, forwarded to the runner.
|
||||
- `options.mode: "check" | "check-write" | "format-write"` — required.
|
||||
- `options.runner: CommandRunner` — required.
|
||||
- `options.clock?: ClockLike` — defaults to `Date.now`.
|
||||
|
||||
Throws `LinterError` with code `INVALID_OPTIONS`, `RUNNER_FAILED`, or `BIOME_FAILED`.
|
||||
|
||||
### `probeBiomeAvailable(options): Promise<boolean>`
|
||||
|
||||
- `options.runner: CommandRunner` — required.
|
||||
- `options.cwd?: string` — defaults to `"."`.
|
||||
|
||||
Resolves to `true` only on `exitCode === 0`; runner rejections resolve to `false`.
|
||||
|
||||
### `nodeCommandRunner: CommandRunner`
|
||||
|
||||
Default runner wrapping `child_process.spawn`. Inherits the current `process.env` and
|
||||
overlays any `env` field provided in the `CommandInput`.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The runner shells through `bunx biome` (not `npx`) so the catalogued
|
||||
`@biomejs/biome` version is the one that runs. Bunx resolves the binary from the
|
||||
current workspace install, matching what `bun run lint` would invoke in every other
|
||||
package.
|
||||
- The default runner buffers stdout / stderr in memory and resolves on `close`, NOT
|
||||
`exit` — `close` fires after both streams are drained, so the report includes
|
||||
trailing output that `exit` would miss.
|
||||
- `RunBiomeOptions` is validated via Zod at the boundary so consumers passing
|
||||
hand-typed strings get an `INVALID_OPTIONS` error rather than a runtime crash deep
|
||||
inside the runner.
|
||||
- `recordBiomeRun(mode, outcome)` and `recordRunDuration("biome", ms)` fire on every
|
||||
successful runner completion (success or failure); the only path that skips them is
|
||||
a runner rejection, which is the `RUNNER_FAILED` branch.
|
||||
- `probeBiomeAvailable` deliberately swallows rejections — a missing binary should not
|
||||
bubble an unhandled rejection through the orchestrator. Callers who want strict
|
||||
behaviour can raise `BIOME_UNAVAILABLE` themselves after a `false` probe.
|
||||
Reference in New Issue
Block a user