Skip to content

Commit 2026134

Browse files
fix(cli.rs): pnpm tauri info exits with error (fix #2509) (#2510)
Co-authored-by: Lucas Nogueira <lucas@tauri.studio>
1 parent 245d12f commit 2026134

File tree

2 files changed

+259
-74
lines changed

2 files changed

+259
-74
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'cli.rs': patch
3+
---
4+
5+
Fixes pnpm error when running `pnpm tauri info`.

tooling/cli.rs/src/info.rs

Lines changed: 254 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,12 @@ struct CargoManifest {
7272
dependencies: HashMap<String, CargoManifestDependency>,
7373
}
7474

75+
enum PackageManager {
76+
Npm,
77+
Pnpm,
78+
Yarn,
79+
}
80+
7581
#[derive(Default)]
7682
pub struct Info;
7783

@@ -86,96 +92,141 @@ fn crate_latest_version(name: &str) -> Option<String> {
8692
}
8793
}
8894

89-
fn npm_latest_version(use_yarn: bool, name: &str) -> crate::Result<Option<String>> {
95+
fn npm_latest_version(pm: &PackageManager, name: &str) -> crate::Result<Option<String>> {
9096
let mut cmd;
91-
if use_yarn {
92-
#[cfg(target_os = "windows")]
93-
{
94-
cmd = Command::new("cmd");
95-
cmd.arg("/c").arg("yarn");
96-
}
97+
match pm {
98+
PackageManager::Yarn => {
99+
#[cfg(target_os = "windows")]
100+
{
101+
cmd = Command::new("cmd");
102+
cmd.arg("/c").arg("yarn");
103+
}
97104

98-
#[cfg(not(target_os = "windows"))]
99-
{
100-
cmd = Command::new("yarn")
101-
}
105+
#[cfg(not(target_os = "windows"))]
106+
{
107+
cmd = Command::new("yarn")
108+
}
102109

103-
let output = cmd
104-
.arg("info")
105-
.arg(name)
106-
.args(&["version", "--json"])
107-
.output()?;
108-
if output.status.success() {
109-
let stdout = String::from_utf8_lossy(&output.stdout);
110-
let info: YarnVersionInfo = serde_json::from_str(&stdout)?;
111-
Ok(Some(info.data.last().unwrap().to_string()))
112-
} else {
113-
Ok(None)
114-
}
115-
} else {
116-
#[cfg(target_os = "windows")]
117-
{
118-
cmd = Command::new("cmd");
119-
cmd.arg("/c").arg("npm");
110+
let output = cmd
111+
.arg("info")
112+
.arg(name)
113+
.args(&["version", "--json"])
114+
.output()?;
115+
if output.status.success() {
116+
let stdout = String::from_utf8_lossy(&output.stdout);
117+
let info: YarnVersionInfo = serde_json::from_str(&stdout)?;
118+
Ok(Some(info.data.last().unwrap().to_string()))
119+
} else {
120+
Ok(None)
121+
}
120122
}
123+
PackageManager::Npm => {
124+
#[cfg(target_os = "windows")]
125+
{
126+
cmd = Command::new("cmd");
127+
cmd.arg("/c").arg("npm");
128+
}
121129

122-
#[cfg(not(target_os = "windows"))]
123-
{
124-
cmd = Command::new("npm")
130+
#[cfg(not(target_os = "windows"))]
131+
{
132+
cmd = Command::new("npm")
133+
}
134+
135+
let output = cmd.arg("show").arg(name).arg("version").output()?;
136+
if output.status.success() {
137+
let stdout = String::from_utf8_lossy(&output.stdout);
138+
Ok(Some(stdout.replace("\n", "")))
139+
} else {
140+
Ok(None)
141+
}
125142
}
143+
PackageManager::Pnpm => {
144+
#[cfg(target_os = "windows")]
145+
{
146+
cmd = Command::new("cmd");
147+
cmd.arg("/c").arg("pnpm");
148+
}
126149

127-
let output = cmd.arg("show").arg(name).arg("version").output()?;
128-
if output.status.success() {
129-
let stdout = String::from_utf8_lossy(&output.stdout);
130-
Ok(Some(stdout.replace("\n", "")))
131-
} else {
132-
Ok(None)
150+
#[cfg(not(target_os = "windows"))]
151+
{
152+
cmd = Command::new("pnpm")
153+
}
154+
155+
let output = cmd.arg("info").arg(name).arg("version").output()?;
156+
if output.status.success() {
157+
let stdout = String::from_utf8_lossy(&output.stdout);
158+
Ok(Some(stdout.replace("\n", "")))
159+
} else {
160+
Ok(None)
161+
}
133162
}
134163
}
135164
}
136165

137166
fn npm_package_version<P: AsRef<Path>>(
138-
use_yarn: bool,
167+
pm: &PackageManager,
139168
name: &str,
140169
app_dir: P,
141170
) -> crate::Result<Option<String>> {
142171
let mut cmd;
143-
let output = if use_yarn {
144-
#[cfg(target_os = "windows")]
145-
{
146-
cmd = Command::new("cmd");
147-
cmd.arg("/c").arg("yarn");
148-
}
172+
let output = match pm {
173+
PackageManager::Yarn => {
174+
#[cfg(target_os = "windows")]
175+
{
176+
cmd = Command::new("cmd");
177+
cmd.arg("/c").arg("yarn");
178+
}
149179

150-
#[cfg(not(target_os = "windows"))]
151-
{
152-
cmd = Command::new("yarn")
153-
}
180+
#[cfg(not(target_os = "windows"))]
181+
{
182+
cmd = Command::new("yarn")
183+
}
154184

155-
cmd
156-
.args(&["list", "--pattern"])
157-
.arg(name)
158-
.args(&["--depth", "0"])
159-
.current_dir(app_dir)
160-
.output()?
161-
} else {
162-
#[cfg(target_os = "windows")]
163-
{
164-
cmd = Command::new("cmd");
165-
cmd.arg("/c").arg("npm");
185+
cmd
186+
.args(&["list", "--pattern"])
187+
.arg(name)
188+
.args(&["--depth", "0"])
189+
.current_dir(app_dir)
190+
.output()?
166191
}
192+
PackageManager::Npm => {
193+
#[cfg(target_os = "windows")]
194+
{
195+
cmd = Command::new("cmd");
196+
cmd.arg("/c").arg("npm");
197+
}
198+
199+
#[cfg(not(target_os = "windows"))]
200+
{
201+
cmd = Command::new("npm")
202+
}
167203

168-
#[cfg(not(target_os = "windows"))]
169-
{
170-
cmd = Command::new("npm")
204+
cmd
205+
.arg("list")
206+
.arg(name)
207+
.args(&["version", "--depth", "0"])
208+
.current_dir(app_dir)
209+
.output()?
171210
}
211+
PackageManager::Pnpm => {
212+
#[cfg(target_os = "windows")]
213+
{
214+
cmd = Command::new("cmd");
215+
cmd.arg("/c").arg("pnpm");
216+
}
217+
218+
#[cfg(not(target_os = "windows"))]
219+
{
220+
cmd = Command::new("pnpm")
221+
}
172222

173-
cmd
174-
.arg("list")
175-
.arg(name)
176-
.args(&["version", "--depth", "0"])
177-
.current_dir(app_dir)
178-
.output()?
223+
cmd
224+
.arg("list")
225+
.arg(name)
226+
.args(&["--parseable", "--depth", "0"])
227+
.current_dir(app_dir)
228+
.output()?
229+
}
179230
};
180231
if output.status.success() {
181232
let stdout = String::from_utf8_lossy(&output.stdout);
@@ -360,9 +411,22 @@ impl Info {
360411
let app_dir = panic::catch_unwind(app_dir).map(Some).unwrap_or_default();
361412
panic::set_hook(hook);
362413

363-
let use_yarn = app_dir
364-
.map(|dir| dir.join("yarn.lock").exists())
365-
.unwrap_or_default();
414+
let mut package_manager = PackageManager::Npm;
415+
if let Some(app_dir) = &app_dir {
416+
let file_names = read_dir(app_dir)
417+
.unwrap()
418+
.filter(|e| {
419+
e.as_ref()
420+
.unwrap()
421+
.metadata()
422+
.unwrap()
423+
.file_type()
424+
.is_file()
425+
})
426+
.map(|e| e.unwrap().file_name().to_string_lossy().into_owned())
427+
.collect::<Vec<String>>();
428+
package_manager = get_package_manager(&file_names)?;
429+
}
366430

367431
if let Some(node_version) = get_version("node", &[]).unwrap_or_default() {
368432
InfoBlock::new("Node.js environment").section().display();
@@ -375,20 +439,21 @@ impl Info {
375439
.display();
376440

377441
VersionBlock::new(" @tauri-apps/cli", metadata.js_cli.version)
378-
.target_version(npm_latest_version(use_yarn, "@tauri-apps/cli").unwrap_or_default())
442+
.target_version(npm_latest_version(&package_manager, "@tauri-apps/cli").unwrap_or_default())
379443
.display();
380444
if let Some(app_dir) = &app_dir {
381445
VersionBlock::new(
382446
" @tauri-apps/api",
383-
npm_package_version(use_yarn, "@tauri-apps/api", app_dir).unwrap_or_default(),
447+
npm_package_version(&package_manager, "@tauri-apps/api", app_dir).unwrap_or_default(),
384448
)
385-
.target_version(npm_latest_version(use_yarn, "@tauri-apps/api").unwrap_or_default())
449+
.target_version(npm_latest_version(&package_manager, "@tauri-apps/api").unwrap_or_default())
386450
.display();
387451
}
388452

389453
InfoBlock::new("Global packages").section().display();
390454

391455
VersionBlock::new(" npm", get_version("npm", &[]).unwrap_or_default()).display();
456+
VersionBlock::new(" pnpm", get_version("pnpm", &[]).unwrap_or_default()).display();
392457
VersionBlock::new(" yarn", get_version("yarn", &[]).unwrap_or_default()).display();
393458
}
394459

@@ -576,3 +641,118 @@ impl Info {
576641
Ok(())
577642
}
578643
}
644+
645+
fn get_package_manager<T: AsRef<str>>(file_names: &[T]) -> crate::Result<PackageManager> {
646+
let mut use_npm = false;
647+
let mut use_pnpm = false;
648+
let mut use_yarn = false;
649+
650+
for name in file_names {
651+
if name.as_ref() == "package-lock.json" {
652+
use_npm = true;
653+
} else if name.as_ref() == "pnpm-lock.yaml" {
654+
use_pnpm = true;
655+
} else if name.as_ref() == "yarn.lock" {
656+
use_yarn = true;
657+
}
658+
}
659+
660+
if !use_npm && !use_pnpm && !use_yarn {
661+
println!("WARNING: no lock files found, defaulting to npm");
662+
return Ok(PackageManager::Npm);
663+
}
664+
665+
let mut found = Vec::new();
666+
667+
if use_npm {
668+
found.push("npm");
669+
}
670+
if use_pnpm {
671+
found.push("pnpm");
672+
}
673+
if use_yarn {
674+
found.push("yarn");
675+
}
676+
677+
if found.len() > 1 {
678+
return Err(anyhow::anyhow!(
679+
"only one package mangager should be used, but found {}\nplease remove unused package manager lock files",
680+
found.join(" and ")
681+
));
682+
}
683+
684+
if use_npm {
685+
Ok(PackageManager::Npm)
686+
} else if use_pnpm {
687+
Ok(PackageManager::Pnpm)
688+
} else {
689+
Ok(PackageManager::Yarn)
690+
}
691+
}
692+
693+
#[cfg(test)]
694+
mod tests {
695+
use crate::info::get_package_manager;
696+
697+
#[test]
698+
fn no_package_manager_lock_file() -> crate::Result<()> {
699+
let file_names = vec!["package.json"];
700+
let pm = get_package_manager(&file_names);
701+
match pm {
702+
Ok(_) => Ok(()),
703+
Err(m) => Err(m),
704+
}
705+
}
706+
707+
#[test]
708+
fn package_managers_npm_and_yarn() -> crate::Result<()> {
709+
let file_names = vec!["package.json", "package-lock.json", "yarn.lock"];
710+
let pm = get_package_manager(&file_names);
711+
match pm {
712+
Ok(_) => panic!("expected error"),
713+
Err(m) => assert_eq!(
714+
m.to_string().as_str(),
715+
"only one package mangager should be used, but found npm and yarn\nplease remove unused package manager lock files"
716+
),
717+
}
718+
Ok(())
719+
}
720+
721+
#[test]
722+
fn package_managers_npm_and_pnpm() -> crate::Result<()> {
723+
let file_names = vec!["package.json", "package-lock.json", "pnpm-lock.yaml"];
724+
let pm = get_package_manager(&file_names);
725+
match pm {
726+
Ok(_) => panic!("expected error"),
727+
Err(m) => assert_eq!(
728+
m.to_string().as_str(),
729+
"only one package mangager should be used, but found npm and pnpm\nplease remove unused package manager lock files"
730+
),
731+
}
732+
Ok(())
733+
}
734+
735+
#[test]
736+
fn package_managers_pnpm_and_yarn() -> crate::Result<()> {
737+
let file_names = vec!["package.json", "pnpm-lock.yaml", "yarn.lock"];
738+
let pm = get_package_manager(&file_names);
739+
match pm {
740+
Ok(_) => panic!("expected error"),
741+
Err(m) => assert_eq!(
742+
m.to_string().as_str(),
743+
"only one package mangager should be used, but found pnpm and yarn\nplease remove unused package manager lock files"
744+
),
745+
}
746+
Ok(())
747+
}
748+
749+
#[test]
750+
fn package_managers_yarn() -> crate::Result<()> {
751+
let file_names = vec!["package.json", "yarn.lock"];
752+
let pm = get_package_manager(&file_names);
753+
match pm {
754+
Ok(_) => Ok(()),
755+
Err(m) => Err(m),
756+
}
757+
}
758+
}

0 commit comments

Comments
 (0)