pi-lens
Version:
Real-time code feedback for pi — LSP, linters, formatters, type-checking, structural analysis & booboo
349 lines (347 loc) • 12.3 kB
JavaScript
import { createRequire as __pilensCreateRequire } from "node:module"; const require = __pilensCreateRequire(import.meta.url);
import {
resolveManagedToolClient
} from "./chunk-7FBF7NPR.js";
import {
biomeConfigArgs
} from "./chunk-HHYAELLJ.js";
import "./chunk-B3I2UUT4.js";
import "./chunk-DIULIWKU.js";
import "./chunk-VDBPVIKD.js";
import "./chunk-TGPXLY2J.js";
import "./chunk-QOZXJ4NA.js";
import "./chunk-PAK6IDME.js";
import "./chunk-GUOGVTCQ.js";
import "./chunk-AJVLWNFN.js";
import "./chunk-YGVYKNSN.js";
import "./chunk-LNAKEO63.js";
import "./chunk-Z3CCVDUB.js";
import "./chunk-2BEMIWEW.js";
import "./chunk-OQGFCZJ4.js";
import {
resolveToolCwd
} from "./chunk-RUOG5D2C.js";
import {
findGlobalBinary
} from "./chunk-U3VGR4VU.js";
import "./chunk-NTUJEGDZ.js";
import {
probeToolAsync
} from "./chunk-EOKRQ3SA.js";
import {
classifyProbeFailure,
createAvailabilityLatch,
describeInstallAttempt,
logAvailabilityDecision,
startHostStallSampler
} from "./chunk-WONERJTP.js";
import "./chunk-22AVIGXS.js";
import "./chunk-GVXA6CCE.js";
import "./chunk-6QIUX4EP.js";
import "./chunk-VHQLPIRJ.js";
import {
getGlobalPiLensDir,
safeSpawnAsync
} from "./chunk-VN2AXLEX.js";
import {
createSingleFlight
} from "./chunk-BW5KFIQC.js";
import "./chunk-QYF5S6P6.js";
import {
isFileKind
} from "./chunk-5ZEJHV35.js";
import "./chunk-DQ2NZ7C4.js";
import "./chunk-VNMT7C64.js";
import "./chunk-2EECBNC5.js";
import "./chunk-N3YQJI6O.js";
import {
createSubsystemLogger
} from "./chunk-O6TQT6RI.js";
import "./chunk-5THXD6X5.js";
import "./chunk-UK3CMEAL.js";
import "./chunk-LJDODBBZ.js";
import "./chunk-ABBOA7UD.js";
import "./chunk-Q5U6FMDI.js";
import "./chunk-NXL4FFQQ.js";
// dist/clients/biome-client.js
import * as fs from "node:fs";
import * as path from "node:path";
var PROBE_TIMEOUT_MS = 1e4;
var BiomeClient = class {
/**
* Availability memo, backed by the shared transient-aware latch (#1476).
*
* Biome is the hot-path formatter: before this, ANY probe failure — a
* timeout included — collapsed to `{ outcome: "missing" }`, which both
* triggered an install and latched `false` for the life of the process. One
* slow first format then disabled formatting until restart.
*/
availabilityLatch = createAvailabilityLatch();
/** Cause of the last probe, read when the resolution comes back. */
lastProbeCause = null;
/**
* Measurements of the last failed probe, held until the latch decides how
* long the verdict lasts. The decision record is emitted after that, so it
* can carry `retryAfterMs` — a latch you can see is worth little if the
* retry schedule is not greppable beside it (#1474).
*/
lastProbeElapsedMs = 0;
lastProbeHostStallMs = 0;
/** Raw facts from the last probe, carried into the decision record (#1500). */
lastProbeEvidence;
// Per-cwd cache of the resolved biome binary. Keying by cwd matters in
// monorepos where different sub-packages each ship their own biome
// installation; sharing one slot across the whole client would cause
// the first resolution to win and stale across other packages.
localBinaryByCwd = /* @__PURE__ */ new Map();
// The binary path written by `ensureTool("biome")` — genuinely global
// (lives under ~/.pi-lens/tools), so it's stored separately from the
// per-cwd cache and used as a final fallback before npx.
autoInstalledBinaryPath = null;
/**
* At-most-one probe/auto-install in flight, via the shared primitive
* (#1753). One instance owns one question, so the key is a constant.
*/
ensureFlight = createSingleFlight();
log;
constructor(verbose = false) {
this.log = verbose ? createSubsystemLogger("biome") : () => {
};
}
/**
* Resolve the fastest available biome binary for `cwd`.
* Prefers local node_modules/.bin/biome (skip npx overhead ~1s).
* Falls back to ~/.pi-lens/tools, then npx.
*
* In monorepos, callers should pass the project / sub-package root for the
* edited file (typically `path.dirname(absolutePath)`). Omitting `cwd`
* falls back to `process.cwd()`, which is wrong when pi is invoked from
* a different directory than the file being edited.
*/
async getBiomeBinary(cwd) {
const resolveCwd = cwd ?? process.cwd();
const cached = this.localBinaryByCwd.get(resolveCwd);
if (cached)
return { cmd: cached, args: [] };
const isWin = process.platform === "win32";
const piLensBin = path.join(getGlobalPiLensDir(), "tools", "node_modules", ".bin");
const candidates = isWin ? [
path.join(resolveCwd, "node_modules", ".bin", "biome.cmd"),
path.join(resolveCwd, "node_modules", ".bin", "biome")
] : [path.join(resolveCwd, "node_modules", ".bin", "biome")];
for (const p of candidates) {
if (fs.existsSync(p)) {
this.localBinaryByCwd.set(resolveCwd, p);
return { cmd: p, args: [] };
}
}
if (this.autoInstalledBinaryPath) {
return { cmd: this.autoInstalledBinaryPath, args: [] };
}
const managedCandidates = isWin ? [path.join(piLensBin, "biome.cmd"), path.join(piLensBin, "biome")] : [path.join(piLensBin, "biome")];
for (const p of managedCandidates) {
if (fs.existsSync(p)) {
this.localBinaryByCwd.set(resolveCwd, p);
return { cmd: p, args: [] };
}
}
const global = await findGlobalBinary("biome");
if (global) {
this.localBinaryByCwd.set(resolveCwd, global);
return { cmd: global, args: [] };
}
return { cmd: "npx", args: ["@biomejs/biome"] };
}
async spawnBiomeAsync(args, timeout = 15e3, cwd) {
const { cmd, args: prefix } = await this.getBiomeBinary(cwd);
return safeSpawnAsync(cmd, [...prefix, ...args], { timeout, cwd });
}
/**
* Ensure Biome is available, auto-installing if necessary.
* Prefer this over isAvailable() for auto-install behavior.
*
* Re-entrancy safe: concurrent first-time callers share one flight, so
* probing/auto-install isn't duplicated. The share and the clear-in-finally
* belong to `singleFlight` (#1753); this method owns only the latch
* short-circuit above it. #1690 exists because the hand-rolled clear here
* was never exercised by a test.
*
* The memo returns `null` when the last verdict was transient and its
* cooldown expired, which re-enters the probe: "biome is not installed" is a
* fact worth caching, "the probe timed out" is a moment worth retrying.
*/
async ensureAvailable() {
const memo = this.availabilityLatch.read();
if (memo !== null)
return memo;
return this.ensureFlight.run("biome", () => this.doEnsureAvailable());
}
/**
* Probe `biome --version` and classify the failure through the shared
* policy. Only a durable verdict (`missing` / `non-installable`) may reach
* the install branch of `resolveManagedToolClient`; a transient one returns
* as `transient`, which that seam passes straight back without installing.
*/
async probeBiome() {
const sampler = startHostStallSampler();
const startedAt = Date.now();
let result;
let hostStallMs;
try {
const { cmd, args: prefix } = await this.getBiomeBinary();
result = await probeToolAsync(cmd, [...prefix, "--version"], {
timeout: PROBE_TIMEOUT_MS
});
} finally {
hostStallMs = sampler.stop();
}
const elapsedMs = Date.now() - startedAt;
if (!result.error && result.status === 0) {
this.lastProbeCause = "ok";
logAvailabilityDecision({
tool: "biome",
verdict: "available",
outcome: "success",
cause: "ok",
elapsedMs,
latched: true,
hostStallMs,
budgetMs: PROBE_TIMEOUT_MS,
classifiedBy: "probe"
});
return { outcome: "success", value: true };
}
const { outcome, cause, evidence } = classifyProbeFailure(result, {
hostStallMs,
unclassifiedFailureOutcome: "missing"
});
const failureOutcome = outcome === "success" ? "non-installable" : outcome;
this.lastProbeCause = cause;
this.lastProbeElapsedMs = elapsedMs;
this.lastProbeHostStallMs = hostStallMs;
this.lastProbeEvidence = evidence;
return { outcome: failureOutcome };
}
async doEnsureAvailable() {
const resolved = await resolveManagedToolClient({
toolId: "biome",
cwd: process.cwd(),
probe: () => this.probeBiome(),
acceptInstalled: (installedPath) => {
this.autoInstalledBinaryPath = installedPath;
return true;
}
});
if (resolved.outcome === "success") {
this.availabilityLatch.noteAvailable();
if (this.lastProbeCause !== null && this.lastProbeCause !== "ok") {
logAvailabilityDecision({
tool: "biome",
verdict: "available",
outcome: "success",
cause: "ok",
elapsedMs: this.lastProbeElapsedMs,
latched: true,
hostStallMs: this.lastProbeHostStallMs,
budgetMs: PROBE_TIMEOUT_MS,
classifiedBy: "caller",
evidence: {
install: "succeeded",
binary: "biome",
source: "managed-dir"
}
});
}
return true;
}
const cause = this.lastProbeCause ?? "not-found";
const retryAfterMs = this.availabilityLatch.noteUnavailable(resolved.outcome, cause);
let installEvidence;
if (resolved.outcome === "missing") {
const { getInstallAttempt } = await import("./probes/installer.js");
installEvidence = describeInstallAttempt(getInstallAttempt("biome"));
}
const evidence = { ...this.lastProbeEvidence, ...installEvidence };
logAvailabilityDecision({
tool: "biome",
verdict: "unavailable",
outcome: resolved.outcome,
cause,
elapsedMs: this.lastProbeElapsedMs,
latched: resolved.outcome !== "transient",
hostStallMs: this.lastProbeHostStallMs,
...retryAfterMs > 0 && { retryAfterMs },
budgetMs: PROBE_TIMEOUT_MS,
// Per arm: a probe-derived verdict says `probe`, while a verdict asserted
// by the install seam — or a cause that fell back because no probe ran —
// says `caller`.
classifiedBy: this.lastProbeCause === null || resolved.outcome === "non-installable" ? "caller" : "probe",
...Object.keys(evidence).length > 0 && { evidence }
});
if (resolved.outcome === "transient") {
this.log("biome availability probe timed out; will retry (not installing)");
}
return false;
}
/**
* Check if a file is supported by Biome
*/
isSupportedFile(filePath) {
return isFileKind(filePath, ["jsts", "json", "css"]);
}
/**
* Async auto-fix variant for pipeline use (non-blocking spawn).
* `cwd` is the dispatch language root (used for config discovery); when
* omitted it defaults to the file's directory.
*/
async fixFileAsync(filePath, cwd) {
if (!await this.ensureAvailable()) {
return {
success: false,
changed: false,
fixed: 0,
error: "Biome not available"
};
}
const absolutePath = path.resolve(filePath);
if (!fs.existsSync(absolutePath)) {
return {
success: false,
changed: false,
fixed: 0,
error: "File not found"
};
}
try {
const before = await fs.promises.readFile(absolutePath, "utf-8");
const configCwd = resolveToolCwd("runner", "biome", absolutePath, {
...cwd !== void 0 && { cwd }
}).cwd;
const result = await this.spawnBiomeAsync(["lint", "--write", ...biomeConfigArgs(configCwd), absolutePath], 15e3, configCwd);
if (result.error) {
return {
success: false,
changed: false,
fixed: 0,
error: result.error.message
};
}
const after = await fs.promises.readFile(absolutePath, "utf-8");
const changed = before !== after;
if (changed) {
this.log(`Fixed issue(s) in ${path.basename(filePath)}`);
}
return { success: true, changed, fixed: changed ? 1 : 0 };
} catch (err) {
return {
success: false,
changed: false,
fixed: 0,
error: err instanceof Error ? err.message : String(err)
};
}
}
};
export {
BiomeClient
};