Skip to content

Commit 6e26543

Browse files
Merge pull request #1171 from charlespierce/npm_global_prefix
Don't intercept npm commands with prefix flag
2 parents 5ca0f68 + a8adb9b commit 6e26543

File tree

1 file changed

+68
-7
lines changed

1 file changed

+68
-7
lines changed

crates/volta-core/src/run/parser.rs

Lines changed: 68 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use crate::inventory::package_configs;
1111
use crate::platform::{Platform, PlatformSpec};
1212
use crate::tool::package::PackageManager;
1313
use crate::tool::Spec;
14+
use log::debug;
1415

1516
const 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
383390
where
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

390408
fn 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

Comments
 (0)