pi-lens
Version:
Real-time code feedback for pi — LSP, linters, formatters, type-checking, structural analysis & booboo
309 lines (306 loc) • 9.49 kB
JavaScript
import { createRequire as __pilensCreateRequire } from "node:module"; const require = __pilensCreateRequire(import.meta.url);
import {
createAvailabilityProbeFlight,
probeToolAsync
} from "./chunk-EOKRQ3SA.js";
import {
classifyProbeFailure,
createAvailabilityLatch,
isTransientDecision,
logAvailabilityDecision,
startHostStallSampler
} from "./chunk-WONERJTP.js";
import {
createGenerationSource,
safeSpawnAsync
} from "./chunk-VN2AXLEX.js";
import {
isAtOrAboveHomeDir,
walkUpDirs
} from "./chunk-Q5U6FMDI.js";
// dist/clients/package-manager.js
import * as fs from "node:fs";
import * as os from "node:os";
import * as path from "node:path";
var PREFERENCE = [
"npm",
"pnpm",
"yarn",
"bun"
];
var packageManagerProbeFlights = createAvailabilityProbeFlight();
function onWindows() {
return process.platform === "win32";
}
function isNodePackageManager(value) {
return value === "npm" || value === "pnpm" || value === "yarn" || value === "bun";
}
function detectNodePackageManager(targetPath) {
if (fs.existsSync(path.join(targetPath, "bun.lockb")) || fs.existsSync(path.join(targetPath, "bun.lock"))) {
return "bun";
}
if (fs.existsSync(path.join(targetPath, "pnpm-lock.yaml"))) {
return "pnpm";
}
if (fs.existsSync(path.join(targetPath, "yarn.lock"))) {
return "yarn";
}
if (fs.existsSync(path.join(targetPath, "package-lock.json"))) {
return "npm";
}
return readPackageManagerField(targetPath);
}
function readPackageManagerField(targetPath) {
try {
const pkgPath = path.join(targetPath, "package.json");
if (!fs.existsSync(pkgPath))
return void 0;
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
if (typeof pkg.packageManager !== "string")
return void 0;
const name = pkg.packageManager.split("@")[0].trim().toLowerCase();
return isNodePackageManager(name) ? name : void 0;
} catch {
return void 0;
}
}
var PROBE_TIMEOUT_MS = 5e3;
var availabilityLatches = /* @__PURE__ */ new Map();
function getLatch(pm) {
let latch = availabilityLatches.get(pm);
if (!latch) {
latch = createAvailabilityLatch();
availabilityLatches.set(pm, latch);
}
return latch;
}
async function probeAvailability(pm) {
const latch = getLatch(pm);
const startedAt = Date.now();
const finder = onWindows() ? "where" : "which";
const sampler = startHostStallSampler();
let result;
let hostStallMs;
try {
result = await probeToolAsync(finder, [pm], { timeout: PROBE_TIMEOUT_MS });
} finally {
hostStallMs = sampler.stop();
}
const elapsedMs = Date.now() - startedAt;
if (result.status === 0 && !result.error) {
latch.noteAvailable();
logAvailabilityDecision({
tool: pm,
verdict: "available",
outcome: "success",
cause: "ok",
elapsedMs,
latched: true,
hostStallMs,
budgetMs: PROBE_TIMEOUT_MS,
classifiedBy: "probe"
});
return true;
}
const { outcome, cause, evidence } = classifyProbeFailure(result, {
hostStallMs,
// Preserve pre-#1496 meaning for anything the classifier can't place: a
// present manager that rejects its probe is durable, same as before.
unclassifiedFailureOutcome: "missing"
});
const retryAfterMs = latch.noteUnavailable(outcome, cause);
logAvailabilityDecision({
tool: pm,
verdict: "unavailable",
outcome,
cause,
elapsedMs,
latched: outcome !== "transient",
hostStallMs,
...retryAfterMs > 0 && { retryAfterMs },
budgetMs: PROBE_TIMEOUT_MS,
classifiedBy: "probe",
evidence
});
return false;
}
function isAvailable(pm, onTransient) {
const latch = getLatch(pm);
const reportIfTransient = (result) => {
if (!result && isTransientDecision({ outcome: latch.getOutcome() })) {
onTransient?.();
}
return result;
};
const memo = latch.read();
if (memo !== null)
return Promise.resolve(reportIfTransient(memo));
const shared = packageManagerProbeFlights.run(`package-manager:${pm}`, () => probeAvailability(pm));
return shared.promise.then(reportIfTransient);
}
function _resetPackageManagerCache() {
packageManagerCacheGeneration.bump();
availabilityLatches.clear();
packageManagerProbeFlights.clear();
globalBinDirCache.clear();
globalBinDirProbeFlights.clear();
}
async function resolveNodePackageManager(cwd = process.cwd()) {
const declared = detectNodePackageManager(cwd);
if (declared && await isAvailable(declared)) {
return declared;
}
for (const pm of PREFERENCE) {
if (await isAvailable(pm))
return pm;
}
return "npm";
}
function pmBinary(pm) {
if (!onWindows())
return pm;
return pm === "bun" ? "bun.exe" : `${pm}.cmd`;
}
function runScriptArgs(script) {
return ["run", script];
}
function installArgs(pm, pkg, options = {}) {
const args = [pm === "npm" ? "install" : "add"];
if (options.ignoreScripts)
args.push("--ignore-scripts");
if (options.legacyPeerDeps && pm === "npm")
args.push("--legacy-peer-deps");
args.push(pkg);
return args;
}
function updateArgs(pm, pkg, options = {}) {
const args = [pm === "yarn" ? "upgrade" : "update"];
if (options.ignoreScripts)
args.push("--ignore-scripts");
args.push(pkg);
return args;
}
var globalBinDirCache = /* @__PURE__ */ new Map();
var packageManagerCacheGeneration = createGenerationSource("package-manager-global-bin");
var globalBinDirProbeFlights = createAvailabilityProbeFlight();
async function globalBinDirsFor(pm) {
const cached = globalBinDirCache.get(pm);
if (cached)
return cached;
const generation = packageManagerCacheGeneration.capture();
const shared = globalBinDirProbeFlights.run(pm, () => probeGlobalBinDirs(pm, generation));
return shared.promise;
}
async function probeGlobalBinDirs(pm, generation) {
if (pm === "bun") {
const base = process.env.BUN_INSTALL || path.join(os.homedir(), ".bun");
const dirs2 = [path.join(base, "bin")];
generation.guardedWrite(pm, () => {
globalBinDirCache.set(pm, dirs2);
});
return dirs2;
}
const query = pm === "npm" ? ["config", "get", "prefix"] : pm === "pnpm" ? ["bin", "-g"] : ["global", "bin"];
const res = await safeSpawnAsync(pmBinary(pm), query, { timeout: 5e3 });
if (res.status !== 0 || res.error)
return [];
const out = res.stdout.trim();
if (!out)
return [];
const dirs = pm === "npm" ? [onWindows() ? out : path.join(out, "bin")] : [out];
generation.guardedWrite(pm, () => {
globalBinDirCache.set(pm, dirs);
});
return dirs;
}
async function allAvailableGlobalBinDirs(onTransient) {
const dirs = [];
const seen = /* @__PURE__ */ new Set();
for (const pm of PREFERENCE) {
if (!await isAvailable(pm, onTransient))
continue;
for (const dir of await globalBinDirsFor(pm)) {
const normalized = path.resolve(dir);
if (seen.has(normalized))
continue;
seen.add(normalized);
dirs.push(normalized);
}
}
return dirs;
}
async function findGlobalBinary(command, windowsExt = ".cmd") {
const candidates = onWindows() ? [`${command}${windowsExt}`, `${command}.exe`, command] : [command];
try {
for (const binDir of await allAvailableGlobalBinDirs()) {
for (const name of candidates) {
const full = path.join(binDir, name);
if (fs.existsSync(full))
return full;
}
}
} catch {
}
return void 0;
}
var NODE_MODULES_BIN_DIRS = [
path.join("node_modules", ".bin")
];
var VENDOR_BIN_DIRS = [path.join("vendor", "bin")];
var VENV_BIN_DIRS = [
path.join(".venv", "bin"),
path.join("venv", "bin"),
path.join(".venv", "Scripts"),
path.join("venv", "Scripts")
];
function expandCandidateNames(tools, windowsExt, isWindows) {
return isWindows && windowsExt !== "" ? tools.flatMap((tool) => [`${tool}${windowsExt}`, tool]) : [...tools];
}
function* matchesInDir(dir, names, binDirs) {
for (const name of names) {
for (const binDir of binDirs) {
const full = path.join(dir, binDir, name);
if (fs.existsSync(full))
yield full;
}
}
}
function* localBinMatches(tools, startDir, options) {
const { windowsExt = ".cmd", homeDir = os.homedir(), binDirs = NODE_MODULES_BIN_DIRS, isWindows = onWindows(), ceiling = true } = options;
const names = expandCandidateNames(tools, windowsExt, isWindows);
for (const dir of walkUpDirs(startDir)) {
if (ceiling && isAtOrAboveHomeDir(dir, homeDir))
return;
yield* matchesInDir(dir, names, binDirs);
}
}
function findLocalBinUpwards(tool, startDir, options = {}) {
return localBinMatches([tool], startDir, options).next().value;
}
function findLocalBinsAt(tools, dir, options = {}) {
const { windowsExt = ".cmd", binDirs = NODE_MODULES_BIN_DIRS, isWindows = onWindows() } = options;
const names = expandCandidateNames(tools, windowsExt, isWindows);
return [...matchesInDir(path.resolve(dir), names, binDirs)];
}
function findLocalBinAt(tool, dir, options = {}) {
return findLocalBinsAt([tool], dir, options)[0];
}
async function findNodeToolBinary(tool, cwd, windowsExt = ".cmd") {
return findLocalBinUpwards(tool, cwd, { windowsExt }) ?? await findGlobalBinary(tool, windowsExt);
}
export {
_resetPackageManagerCache,
resolveNodePackageManager,
pmBinary,
runScriptArgs,
installArgs,
updateArgs,
allAvailableGlobalBinDirs,
findGlobalBinary,
VENDOR_BIN_DIRS,
VENV_BIN_DIRS,
findLocalBinUpwards,
findLocalBinsAt,
findLocalBinAt,
findNodeToolBinary
};