Skip to content

Commit 650d694

Browse files
committed
Fix pnpm version detection to respect packageManager field
Both pnpm 9 and 10 use lockfileVersion '9.0', making them indistinguishable by lockfile alone. This fix prioritizes the packageManager field in package.json when determining which pnpm version to use. Changes: - Parse packageManager field (e.g., "[email protected]") to extract version - Map parsed version to appropriate nix package (pnpm-9_x, pnpm-10_x) - Fall back to lockfile detection only when packageManager is absent - Default lockfileVersion '9.0' to pnpm-9_x (when introduced) - Add packageManager field to node-24-pnpm-10 example for pnpm 10 - Update test snapshots This ensures pnpm 10 is only used when explicitly specified in the packageManager field, fixing the node-pnpm-corepack example which was incorrectly using pnpm-10_x instead of pnpm-9_x.
1 parent 5f10470 commit 650d694

File tree

7 files changed

+112
-24
lines changed

7 files changed

+112
-24
lines changed

examples/node-24-pnpm-10/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,6 @@
77
},
88
"engines": {
99
"node": "24.x"
10-
}
10+
},
11+
"packageManager": "[email protected]"
1112
}

src/providers/node/mod.rs

Lines changed: 51 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -515,18 +515,18 @@ impl NodeProvider {
515515
}
516516

517517
// Extract version number from package name (e.g., "nodejs_18" -> 18)
518-
let version = node_pkg.name
518+
let version = node_pkg
519+
.name
519520
.strip_prefix("nodejs_")
520521
.and_then(|v| v.parse::<u32>().ok())
521522
.unwrap_or(DEFAULT_NODE_VERSION);
522523

523524
// Look up the archive for this version
524-
let archive = version_number_to_archive(version)
525-
.unwrap_or_else(|| {
526-
// Fallback to default version's archive
527-
version_number_to_archive(DEFAULT_NODE_VERSION)
528-
.expect("Default node version must exist in AVAILABLE_NODE_VERSIONS")
529-
});
525+
let archive = version_number_to_archive(version).unwrap_or_else(|| {
526+
// Fallback to default version's archive
527+
version_number_to_archive(DEFAULT_NODE_VERSION)
528+
.expect("Default node version must exist in AVAILABLE_NODE_VERSIONS")
529+
});
530530

531531
Ok(archive.to_string())
532532
}
@@ -547,18 +547,35 @@ impl NodeProvider {
547547
pkgs.push(node_pkg);
548548

549549
if package_manager == "pnpm" {
550-
let lockfile = app.read_file("pnpm-lock.yaml").unwrap_or_default();
551-
if lockfile.starts_with("lockfileVersion: 5.3") {
552-
pm_pkg = Pkg::new("pnpm-6_x");
553-
} else if lockfile.starts_with("lockfileVersion: 5.4") {
554-
pm_pkg = Pkg::new("pnpm-7_x");
555-
} else if lockfile.starts_with("lockfileVersion: '6.0'") {
556-
pm_pkg = Pkg::new("pnpm-8_x");
557-
} else if lockfile.starts_with("lockfileVersion: '9.0'") {
558-
pm_pkg = Pkg::new("pnpm-10_x");
550+
// First, try to determine version from packageManager field (for corepack)
551+
if let Some(ref pkg_manager_field) = package_json.package_manager {
552+
if let Some(version_str) = pkg_manager_field.strip_prefix("pnpm@") {
553+
// Parse major version from "[email protected]" -> 9
554+
if let Some(major_version) = version_str.split('.').next() {
555+
if let Ok(major) = major_version.parse::<u32>() {
556+
pm_pkg = match major {
557+
6 => Pkg::new("pnpm-6_x"),
558+
7 => Pkg::new("pnpm-7_x"),
559+
8 => Pkg::new("pnpm-8_x"),
560+
9 => Pkg::new("pnpm-9_x"),
561+
10 => Pkg::new("pnpm-10_x"),
562+
_ => {
563+
// For unknown versions, try lockfile detection
564+
NodeProvider::get_pnpm_package_from_lockfile(app)
565+
}
566+
};
567+
} else {
568+
pm_pkg = NodeProvider::get_pnpm_package_from_lockfile(app);
569+
}
570+
} else {
571+
pm_pkg = NodeProvider::get_pnpm_package_from_lockfile(app);
572+
}
573+
} else {
574+
pm_pkg = NodeProvider::get_pnpm_package_from_lockfile(app);
575+
}
559576
} else {
560-
// Default to pnpm 9
561-
pm_pkg = Pkg::new("pnpm-9_x");
577+
// Fall back to lockfile-based detection
578+
pm_pkg = NodeProvider::get_pnpm_package_from_lockfile(app);
562579
}
563580
} else if package_manager == "yarn" {
564581
pm_pkg = Pkg::new("yarn-1_x");
@@ -667,6 +684,21 @@ impl NodeProvider {
667684
all_deps
668685
}
669686

687+
fn get_pnpm_package_from_lockfile(app: &App) -> Pkg {
688+
let lockfile = app.read_file("pnpm-lock.yaml").unwrap_or_default();
689+
if lockfile.starts_with("lockfileVersion: 5.3") {
690+
Pkg::new("pnpm-6_x")
691+
} else if lockfile.starts_with("lockfileVersion: 5.4") {
692+
Pkg::new("pnpm-7_x")
693+
} else if lockfile.starts_with("lockfileVersion: '6.0'") {
694+
Pkg::new("pnpm-8_x")
695+
} else if lockfile.starts_with("lockfileVersion: '9.0'") {
696+
Pkg::new("pnpm-9_x")
697+
} else {
698+
Pkg::new("pnpm-9_x")
699+
}
700+
}
701+
670702
pub fn cache_tsbuildinfo_file(app: &App, build: &mut Phase) {
671703
let mut ts_config: TsConfigJson = app.read_json("tsconfig.json").unwrap_or_default();
672704
if let Some(ref extends) = ts_config.extends {
@@ -831,7 +863,7 @@ mod test {
831863
&App::new("examples/node")?,
832864
&Environment::default()
833865
)?,
834-
Pkg::new(version_number_to_pkg(22).as_str())
866+
Pkg::new(version_number_to_pkg(24).as_str())
835867
);
836868

837869
Ok(())
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
---
2+
source: tests/generate_plan_tests.rs
3+
expression: plan
4+
---
5+
{
6+
"providers": [],
7+
"buildImage": "[build_image]",
8+
"variables": {
9+
"CI": "true",
10+
"NIXPACKS_METADATA": "node",
11+
"NODE_ENV": "production",
12+
"NPM_CONFIG_PRODUCTION": "false"
13+
},
14+
"phases": {
15+
"build": {
16+
"name": "build",
17+
"dependsOn": [
18+
"install"
19+
],
20+
"cacheDirectories": [
21+
"node_modules/.cache"
22+
]
23+
},
24+
"install": {
25+
"name": "install",
26+
"dependsOn": [
27+
"setup"
28+
],
29+
"cmds": [
30+
"npm install -g [email protected] && corepack enable",
31+
"pnpm i --frozen-lockfile"
32+
],
33+
"cacheDirectories": [
34+
"/root/.local/share/pnpm/store/v3"
35+
],
36+
"paths": [
37+
"/app/node_modules/.bin"
38+
]
39+
},
40+
"setup": {
41+
"name": "setup",
42+
"nixPkgs": [
43+
"nodejs_24",
44+
"pnpm-10_x"
45+
],
46+
"nixOverlays": [
47+
"https://github.com/railwayapp/nix-npm-overlay/archive/main.tar.gz"
48+
],
49+
"nixpkgsArchive": "[archive]"
50+
}
51+
},
52+
"start": {
53+
"cmd": "pnpm run start"
54+
}
55+
}

tests/snapshots/generate_plan_tests__node_pnpm_corepack.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ expression: plan
4343
"setup": {
4444
"name": "setup",
4545
"nixPkgs": [
46-
"nodejs_22",
46+
"nodejs_24",
4747
"pnpm-9_x"
4848
],
4949
"nixOverlays": [

tests/snapshots/generate_plan_tests__node_pnpm_monorepo.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ expression: plan
4545
"setup": {
4646
"name": "setup",
4747
"nixPkgs": [
48-
"nodejs_22",
48+
"nodejs_24",
4949
"pnpm-9_x"
5050
],
5151
"nixOverlays": [

tests/snapshots/generate_plan_tests__node_remix.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ expression: plan
4343
"setup": {
4444
"name": "setup",
4545
"nixPkgs": [
46-
"nodejs_22",
46+
"nodejs_24",
4747
"npm-9_x"
4848
],
4949
"nixOverlays": [

tests/snapshots/generate_plan_tests__node_turborepo.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ expression: plan
4444
"setup": {
4545
"name": "setup",
4646
"nixPkgs": [
47-
"nodejs_22",
47+
"nodejs_24",
4848
"npm-8_x"
4949
],
5050
"nixOverlays": [

0 commit comments

Comments
 (0)