fix(install): detect package manager launcher names

This commit is contained in:
Vincent Koc
2026-06-21 15:10:58 +02:00
parent 5693fcda78
commit 11a2e03bd4
2 changed files with 70 additions and 15 deletions
+30 -15
View File
@@ -2,6 +2,10 @@
import { pathToFileURL } from "node:url";
const allowedLifecyclePackageManagers = new Set(["pnpm", "npm", "yarn", "bun"]);
const lifecyclePackageManagerLauncherAliases = new Map([
["yarnpkg", "yarn"],
["yarn-berry", "yarn"],
]);
function normalizeEnvValue(value) {
return typeof value === "string" ? value.trim() : "";
@@ -15,6 +19,31 @@ function normalizeLifecyclePackageManagerName(value) {
return allowedLifecyclePackageManagers.has(normalized) ? normalized : null;
}
function detectLifecyclePackageManagerFromExecPath(value) {
const execPath = normalizeEnvValue(value).toLowerCase();
const executableName = execPath.split(/[\\/]/u).findLast((segment) => segment.length > 0) ?? "";
const launcherName = executableName.replace(/\.(?:c?js|mjs|cmd|ps1|exe)$/u, "");
const candidates = [launcherName, launcherName.replace(/-cli$/u, "")];
for (const candidate of candidates) {
if (/^yarn(?:pkg)?-\d/u.test(candidate)) {
return "yarn";
}
const aliasedPackageManager = lifecyclePackageManagerLauncherAliases.get(candidate);
if (aliasedPackageManager) {
return aliasedPackageManager;
}
const packageManager = normalizeLifecyclePackageManagerName(candidate);
if (packageManager) {
return packageManager;
}
}
return null;
}
/**
* Detects the package manager running the current lifecycle script.
*/
@@ -25,21 +54,7 @@ export function detectLifecyclePackageManager(env = process.env) {
return normalizeLifecyclePackageManagerName(userAgentMatch[1]);
}
const execPath = normalizeEnvValue(env.npm_execpath).toLowerCase();
if (execPath.includes("pnpm")) {
return "pnpm";
}
if (execPath.includes("npm")) {
return "npm";
}
if (execPath.includes("yarn")) {
return "yarn";
}
if (execPath.includes("bun")) {
return "bun";
}
return null;
return detectLifecyclePackageManagerFromExecPath(env.npm_execpath);
}
/**
@@ -35,6 +35,46 @@ describe("detectLifecyclePackageManager", () => {
).toBe("pnpm");
});
it("detects npm cli launchers from npm_execpath", () => {
expect(
detectLifecyclePackageManager({
npm_execpath: "C:\\Tools\\nodejs\\node_modules\\npm\\bin\\npm-cli.js",
}),
).toBe("npm");
});
it("detects yarnpkg launchers from npm_execpath", () => {
expect(
detectLifecyclePackageManager({
npm_execpath: "C:\\Tools\\corepack\\yarnpkg.cmd",
}),
).toBe("yarn");
});
it("detects versioned Yarn release launchers from npm_execpath", () => {
expect(
detectLifecyclePackageManager({
npm_execpath: "/work/project/.yarn/releases/yarn-4.5.0.cjs",
}),
).toBe("yarn");
});
it("detects Yarn Berry release launchers from npm_execpath", () => {
expect(
detectLifecyclePackageManager({
npm_execpath: "/work/project/.yarn/releases/yarn-berry.cjs",
}),
).toBe("yarn");
});
it("ignores package manager names in npm_execpath parent directories", () => {
expect(
detectLifecyclePackageManager({
npm_execpath: "/tmp/npm-cache/bin/yarn.js",
}),
).toBe("yarn");
});
it("ignores untrusted user-agent tokens with control characters", () => {
expect(
detectLifecyclePackageManager({