Skip to content

Commit c51f962

Browse files
authored
feat: improve detecting pm and support bun (electron-userland#9262)
The previous logic determined it was pnpm by checking if pnpm existed in process.env. Actually, we should establish a priority: first check npm_config_user_agent, after all checks are completed, then check exec path, and finally check packageManager, which would be more accurate.
1 parent 02e59ba commit c51f962

File tree

3 files changed

+59
-29
lines changed

3 files changed

+59
-29
lines changed

.changeset/rare-numbers-join.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"app-builder-lib": patch
3+
---
4+
5+
fix: enhance package manager detection to support Yarn Berry (#9261)

packages/app-builder-lib/src/node-module-collector/index.ts

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { NpmNodeModulesCollector } from "./npmNodeModulesCollector"
22
import { PnpmNodeModulesCollector } from "./pnpmNodeModulesCollector"
33
import { YarnNodeModulesCollector } from "./yarnNodeModulesCollector"
4-
import { detectPackageManagerByLockfile, detectPackageManagerByEnv, PM, getPackageManagerCommand } from "./packageManager"
4+
import { detectPackageManagerByLockfile, detectPackageManagerByEnv, PM, getPackageManagerCommand, detectYarnBerry } from "./packageManager"
55
import { NodeModuleInfo } from "./types"
66
import { TmpDir } from "temp-file"
77

@@ -13,6 +13,7 @@ export async function getCollectorByPackageManager(pm: PM, rootDir: string, temp
1313
}
1414
return new PnpmNodeModulesCollector(rootDir, tempDirManager)
1515
case PM.NPM:
16+
case PM.BUN:
1617
return new NpmNodeModulesCollector(rootDir, tempDirManager)
1718
case PM.YARN:
1819
return new YarnNodeModulesCollector(rootDir, tempDirManager)
@@ -29,14 +30,27 @@ export async function getNodeModules(pm: PM, rootDir: string, tempDirManager: Tm
2930
export function detectPackageManager(dirs: string[]): PM {
3031
let pm: PM | null = null
3132

33+
const resolveYarnVersion = (pm: PM) => {
34+
if (pm === PM.YARN) {
35+
return detectYarnBerry()
36+
}
37+
return pm
38+
}
39+
3240
for (const dir of dirs) {
3341
pm = detectPackageManagerByLockfile(dir)
3442
if (pm) {
35-
return pm
43+
return resolveYarnVersion(pm)
3644
}
3745
}
3846

39-
return detectPackageManagerByEnv("pnpm") || detectPackageManagerByEnv("yarn") || detectPackageManagerByEnv("npm") || PM.NPM
47+
pm = detectPackageManagerByEnv()
48+
if (pm) {
49+
return resolveYarnVersion(pm)
50+
}
51+
52+
// Default to npm
53+
return PM.NPM
4054
}
4155

4256
export { PM, getPackageManagerCommand }
Lines changed: 37 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
import * as path from "path"
22
import * as fs from "fs"
33
import * as which from "which"
4+
import { execSync } from "child_process"
45

56
export enum PM {
67
NPM = "npm",
78
YARN = "yarn",
89
PNPM = "pnpm",
910
YARN_BERRY = "yarn-berry",
11+
BUN = "bun",
1012
}
1113

1214
// Cache for resolved paths
@@ -15,6 +17,7 @@ const pmPathCache: Record<PM, string | null | undefined> = {
1517
[PM.YARN]: undefined,
1618
[PM.PNPM]: undefined,
1719
[PM.YARN_BERRY]: undefined,
20+
[PM.BUN]: undefined,
1821
}
1922

2023
function resolveCommand(pm: PM): string {
@@ -42,46 +45,54 @@ export function getPackageManagerCommand(pm: PM) {
4245
return resolved
4346
}
4447

45-
export function detectPackageManagerByEnv(pm: "npm" | "yarn" | "pnpm"): PM | null {
46-
const ua = process.env.npm_config_user_agent ?? ""
47-
const execPath = process.env.npm_execpath?.toLowerCase() ?? ""
48+
export function detectPackageManagerByEnv(): PM | null {
49+
const packageJsonPath = path.join(process.cwd(), "package.json")
50+
const packageManager = fs.existsSync(packageJsonPath) ? JSON.parse(fs.readFileSync(packageJsonPath, "utf8"))?.packageManager : undefined
4851

49-
const yarnVersion = process.env.YARN_VERSION
50-
const isBerry = yarnVersion?.startsWith("2.") || yarnVersion?.startsWith("3.")
52+
const priorityChecklist = [
53+
(key: string) => process.env.npm_config_user_agent?.includes(key),
54+
(key: string) => process.env.npm_execpath?.includes(key),
55+
(key: string) => packageManager?.startsWith(`${key}@`),
56+
]
5157

52-
switch (pm) {
53-
case "pnpm":
54-
return ua.includes("pnpm") || execPath.includes("pnpm") || process.env.PNPM_HOME ? PM.PNPM : null
55-
case "yarn":
56-
if (ua.includes("yarn") || execPath.includes("yarn") || process.env.YARN_REGISTRY) {
57-
return isBerry || ua.includes("yarn/2") || ua.includes("yarn/3") ? PM.YARN_BERRY : PM.YARN
58+
const pms = Object.values(PM).filter(pm => pm !== PM.YARN_BERRY)
59+
for (const checker of priorityChecklist) {
60+
for (const pm of pms) {
61+
if (checker(pm)) {
62+
return pm
5863
}
59-
return null
60-
case "npm":
61-
return ua.includes("npm") || execPath.includes("npm") || process.env.npm_package_json ? PM.NPM : null
62-
default:
63-
return null
64+
}
6465
}
66+
return null
6567
}
6668

6769
export function detectPackageManagerByLockfile(cwd: string): PM | null {
6870
const has = (file: string) => fs.existsSync(path.join(cwd, file))
6971

70-
const yarn = has("yarn.lock")
71-
const pnpm = has("pnpm-lock.yaml")
72-
const npm = has("package-lock.json")
73-
7472
const detected: PM[] = []
75-
if (yarn) detected.push(PM.YARN)
76-
if (pnpm) detected.push(PM.PNPM)
77-
if (npm) detected.push(PM.NPM)
73+
if (has("yarn.lock")) {
74+
detected.push(PM.YARN)
75+
}
76+
if (has("pnpm-lock.yaml")) {
77+
detected.push(PM.PNPM)
78+
}
79+
if (has("package-lock.json")) {
80+
detected.push(PM.NPM)
81+
}
82+
if (has("bun.lock") || has("bun.lockb")) {
83+
detected.push(PM.BUN)
84+
}
7885

7986
if (detected.length === 1) {
80-
if (detected[0] === PM.YARN) {
81-
return detectPackageManagerByEnv("yarn") === PM.YARN_BERRY ? PM.YARN_BERRY : PM.YARN
82-
}
8387
return detected[0]
8488
}
8589

8690
return null
8791
}
92+
93+
export function detectYarnBerry() {
94+
// yarn --version
95+
const version = execSync("yarn --version").toString().trim()
96+
if (parseInt(version.split(".")[0]) > 1) return PM.YARN_BERRY
97+
return PM.YARN
98+
}

0 commit comments

Comments
 (0)