Skip to content

Commit 11a2e03

Browse files
committed
fix(install): detect package manager launcher names
1 parent 5693fcd commit 11a2e03

2 files changed

Lines changed: 70 additions & 15 deletions

File tree

scripts/preinstall-package-manager-warning.mjs

Lines changed: 30 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22
import { pathToFileURL } from "node:url";
33

44
const allowedLifecyclePackageManagers = new Set(["pnpm", "npm", "yarn", "bun"]);
5+
const lifecyclePackageManagerLauncherAliases = new Map([
6+
["yarnpkg", "yarn"],
7+
["yarn-berry", "yarn"],
8+
]);
59

610
function normalizeEnvValue(value) {
711
return typeof value === "string" ? value.trim() : "";
@@ -15,6 +19,31 @@ function normalizeLifecyclePackageManagerName(value) {
1519
return allowedLifecyclePackageManagers.has(normalized) ? normalized : null;
1620
}
1721

22+
function detectLifecyclePackageManagerFromExecPath(value) {
23+
const execPath = normalizeEnvValue(value).toLowerCase();
24+
const executableName = execPath.split(/[\\/]/u).findLast((segment) => segment.length > 0) ?? "";
25+
const launcherName = executableName.replace(/\.(?:c?js|mjs|cmd|ps1|exe)$/u, "");
26+
const candidates = [launcherName, launcherName.replace(/-cli$/u, "")];
27+
28+
for (const candidate of candidates) {
29+
if (/^yarn(?:pkg)?-\d/u.test(candidate)) {
30+
return "yarn";
31+
}
32+
33+
const aliasedPackageManager = lifecyclePackageManagerLauncherAliases.get(candidate);
34+
if (aliasedPackageManager) {
35+
return aliasedPackageManager;
36+
}
37+
38+
const packageManager = normalizeLifecyclePackageManagerName(candidate);
39+
if (packageManager) {
40+
return packageManager;
41+
}
42+
}
43+
44+
return null;
45+
}
46+
1847
/**
1948
* Detects the package manager running the current lifecycle script.
2049
*/
@@ -25,21 +54,7 @@ export function detectLifecyclePackageManager(env = process.env) {
2554
return normalizeLifecyclePackageManagerName(userAgentMatch[1]);
2655
}
2756

28-
const execPath = normalizeEnvValue(env.npm_execpath).toLowerCase();
29-
if (execPath.includes("pnpm")) {
30-
return "pnpm";
31-
}
32-
if (execPath.includes("npm")) {
33-
return "npm";
34-
}
35-
if (execPath.includes("yarn")) {
36-
return "yarn";
37-
}
38-
if (execPath.includes("bun")) {
39-
return "bun";
40-
}
41-
42-
return null;
57+
return detectLifecyclePackageManagerFromExecPath(env.npm_execpath);
4358
}
4459

4560
/**

test/scripts/preinstall-package-manager-warning.test.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,46 @@ describe("detectLifecyclePackageManager", () => {
3535
).toBe("pnpm");
3636
});
3737

38+
it("detects npm cli launchers from npm_execpath", () => {
39+
expect(
40+
detectLifecyclePackageManager({
41+
npm_execpath: "C:\\Tools\\nodejs\\node_modules\\npm\\bin\\npm-cli.js",
42+
}),
43+
).toBe("npm");
44+
});
45+
46+
it("detects yarnpkg launchers from npm_execpath", () => {
47+
expect(
48+
detectLifecyclePackageManager({
49+
npm_execpath: "C:\\Tools\\corepack\\yarnpkg.cmd",
50+
}),
51+
).toBe("yarn");
52+
});
53+
54+
it("detects versioned Yarn release launchers from npm_execpath", () => {
55+
expect(
56+
detectLifecyclePackageManager({
57+
npm_execpath: "/work/project/.yarn/releases/yarn-4.5.0.cjs",
58+
}),
59+
).toBe("yarn");
60+
});
61+
62+
it("detects Yarn Berry release launchers from npm_execpath", () => {
63+
expect(
64+
detectLifecyclePackageManager({
65+
npm_execpath: "/work/project/.yarn/releases/yarn-berry.cjs",
66+
}),
67+
).toBe("yarn");
68+
});
69+
70+
it("ignores package manager names in npm_execpath parent directories", () => {
71+
expect(
72+
detectLifecyclePackageManager({
73+
npm_execpath: "/tmp/npm-cache/bin/yarn.js",
74+
}),
75+
).toBe("yarn");
76+
});
77+
3878
it("ignores untrusted user-agent tokens with control characters", () => {
3979
expect(
4080
detectLifecyclePackageManager({

0 commit comments

Comments
 (0)