@@ -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 ) ]
7682pub 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
137166fn 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 {}\n please 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\n please 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\n please 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\n please 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