@@ -11,6 +11,7 @@ use crate::inventory::package_configs;
1111use crate :: platform:: { Platform , PlatformSpec } ;
1212use crate :: tool:: package:: PackageManager ;
1313use crate :: tool:: Spec ;
14+ use log:: debug;
1415
1516const UNSAFE_GLOBAL : & str = "VOLTA_UNSAFE_GLOBAL" ;
1617/// Aliases that npm supports for the 'install' command
@@ -51,7 +52,7 @@ impl<'a> CommandArg<'a> {
5152 // then we treat the command not a global and allow npm to handle any error messages.
5253 match positionals. next ( ) {
5354 Some ( cmd) if NPM_INSTALL_ALIASES . iter ( ) . any ( |a| a == & cmd) => {
54- if has_global_flag ( args) {
55+ if has_global_without_prefix ( args) {
5556 let tools: Vec < _ > = positionals. collect ( ) ;
5657
5758 if tools. is_empty ( ) {
@@ -72,7 +73,7 @@ impl<'a> CommandArg<'a> {
7273 }
7374 }
7475 Some ( cmd) if NPM_UNINSTALL_ALIASES . iter ( ) . any ( |a| a == & cmd) => {
75- if has_global_flag ( args) {
76+ if has_global_without_prefix ( args) {
7677 let tools: Vec < _ > = positionals. collect ( ) ;
7778
7879 if tools. is_empty ( ) {
@@ -90,7 +91,7 @@ impl<'a> CommandArg<'a> {
9091 if tools. is_empty ( ) {
9192 // `npm unlink` without any arguments is used to unlink the current project
9293 CommandArg :: Intercepted ( InterceptedCommand :: Unlink )
93- } else if has_global_flag ( args) {
94+ } else if has_global_without_prefix ( args) {
9495 // With arguments, `npm unlink` is an alias of `npm remove`
9596 CommandArg :: Global ( GlobalCommand :: Uninstall ( UninstallArgs { tools } ) )
9697 } else {
@@ -106,7 +107,7 @@ impl<'a> CommandArg<'a> {
106107 CommandArg :: Intercepted ( InterceptedCommand :: Link ( LinkArgs { common_args, tools } ) )
107108 }
108109 Some ( cmd) if NPM_UPDATE_ALIASES . iter ( ) . any ( |a| a == & cmd) => {
109- if has_global_flag ( args) {
110+ if has_global_without_prefix ( args) {
110111 // Once again, the common args are the command combined with any flags
111112 let mut common_args = vec ! [ cmd] ;
112113 common_args. extend ( args. iter ( ) . filter ( is_flag) . map ( AsRef :: as_ref) ) ;
@@ -379,12 +380,29 @@ impl<'a> LinkArgs<'a> {
379380 }
380381}
381382
382- fn has_global_flag < A > ( args : & [ A ] ) -> bool
383+ /// Check if the provided argument list includes a global flag and _doesn't_ have a prefix setting
384+ ///
385+ /// For our interception, we only want to intercept global commands. Additionally, if the user
386+ /// passes a prefix setting, that will override the logic we use to redirect the install, so our
387+ /// process won't work and will cause an error. We should avoid intercepting in those cases since
388+ /// a command with an explicit prefix is something beyond the "standard" global install anyway.
389+ fn has_global_without_prefix < A > ( args : & [ A ] ) -> bool
383390where
384391 A : AsRef < OsStr > ,
385392{
386- args. iter ( )
387- . any ( |arg| arg. as_ref ( ) == "-g" || arg. as_ref ( ) == "--global" )
393+ let ( has_global, has_prefix) = args. iter ( ) . fold ( ( false , false ) , |( global, prefix) , arg| {
394+ match arg. as_ref ( ) . to_str ( ) {
395+ Some ( "-g" ) | Some ( "--global" ) => ( true , prefix) ,
396+ Some ( pre) if pre. starts_with ( "--prefix" ) => ( global, true ) ,
397+ _ => ( global, prefix) ,
398+ }
399+ } ) ;
400+
401+ if has_global && has_prefix {
402+ debug ! ( "Skipping global interception due to prefix argument" ) ;
403+ }
404+
405+ has_global && !has_prefix
388406}
389407
390408fn is_flag < A > ( arg : & A ) -> bool
@@ -685,6 +703,49 @@ mod tests {
685703 _ => panic ! ( "Doesn't parse uninstall with extra flags as a global" ) ,
686704 }
687705 }
706+
707+ #[ test]
708+ fn skips_commands_with_prefix ( ) {
709+ match CommandArg :: for_npm ( & arg_list ( & [ "install" , "-g" , "--prefix" , "~/" , "ember" ] ) ) {
710+ CommandArg :: Standard => { }
711+ _ => panic ! ( "Parsed command with prefix as a global" ) ,
712+ }
713+
714+ match CommandArg :: for_npm ( & arg_list ( & [ "install" , "-g" , "--prefix=~/" , "ember" ] ) ) {
715+ CommandArg :: Standard => { }
716+ _ => panic ! ( "Parsed command with prefix as a global" ) ,
717+ }
718+
719+ match CommandArg :: for_npm ( & arg_list ( & [ "uninstall" , "-g" , "--prefix" , "~/" , "ember" ] ) ) {
720+ CommandArg :: Standard => { }
721+ _ => panic ! ( "Parsed command with prefix as a global" ) ,
722+ }
723+
724+ match CommandArg :: for_npm ( & arg_list ( & [ "uninstall" , "-g" , "--prefix=~/" , "ember" ] ) ) {
725+ CommandArg :: Standard => { }
726+ _ => panic ! ( "Parsed command with prefix as a global" ) ,
727+ }
728+
729+ match CommandArg :: for_npm ( & arg_list ( & [ "unlink" , "-g" , "--prefix" , "~/" , "ember" ] ) ) {
730+ CommandArg :: Standard => { }
731+ _ => panic ! ( "Parsed command with prefix as a global" ) ,
732+ }
733+
734+ match CommandArg :: for_npm ( & arg_list ( & [ "unlink" , "-g" , "--prefix=~/" , "ember" ] ) ) {
735+ CommandArg :: Standard => { }
736+ _ => panic ! ( "Parsed command with prefix as a global" ) ,
737+ }
738+
739+ match CommandArg :: for_npm ( & arg_list ( & [ "update" , "-g" , "--prefix" , "~/" ] ) ) {
740+ CommandArg :: Standard => { }
741+ _ => panic ! ( "Parsed command with prefix as a global" ) ,
742+ }
743+
744+ match CommandArg :: for_npm ( & arg_list ( & [ "update" , "-g" , "--prefix=~/" ] ) ) {
745+ CommandArg :: Standard => { }
746+ _ => panic ! ( "Parsed command with prefix as a global" ) ,
747+ }
748+ }
688749 }
689750
690751 mod yarn {
0 commit comments