Skip to content

Commit

Permalink
new: Error when installing a global with npm/pnpm/yarn directly. (#110)
Browse files Browse the repository at this point in the history
  • Loading branch information
milesj authored May 25, 2023
1 parent 979c720 commit ba5e851
Show file tree
Hide file tree
Showing 5 changed files with 133 additions and 1 deletion.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## Unreleased

#### 🚀 Updates

- Updated npm/pnpm/yarn to error when attempting to install a global binary. Use `proto install-global` instead.

#### ⚙️ Internal

- Improved handling of alternate tool binaries, like `npx` and `node-gyp`.
Expand Down
8 changes: 7 additions & 1 deletion crates/cli/src/commands/run.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::commands::install::install;
use crate::hooks::node as node_hooks;
use crate::states::UserConfig;
use crate::tools::{create_tool, ToolType};
use proto_core::{color, detect_version, ProtoError};
Expand Down Expand Up @@ -31,7 +32,7 @@ pub async fn run(
debug!("Auto-install setting is configured, attempting to install");

install(
tool_type,
tool_type.clone(),
Some(tool.get_resolved_version().to_owned()),
false,
vec![],
Expand Down Expand Up @@ -74,6 +75,11 @@ pub async fn run(

debug!(bin = %bin_path.display(), "Running {}", tool.get_name());

// Trigger before hook
if matches!(tool_type, ToolType::Npm | ToolType::Pnpm | ToolType::Yarn) {
node_hooks::pre_run(tool_type, &args).await?;
}

// Run the command
let status = Command::new(bin_path)
.args(&args)
Expand Down
1 change: 1 addition & 0 deletions crates/cli/src/hooks/mod.rs
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
pub mod go;
pub mod node;
43 changes: 43 additions & 0 deletions crates/cli/src/hooks/node.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
use crate::tools::{create_tool, ToolType};
use proto_core::{color, ProtoError};

pub async fn pre_run(tool_type: ToolType, args: &[String]) -> Result<(), ProtoError> {
let mut is_install_command = false;
let mut is_global = false;

match tool_type {
// npm install -g <dep>
// pnpm add -g <dep>
ToolType::Npm | ToolType::Pnpm => {
is_install_command = args[0] == "install" || args[0] == "i" || args[0] == "add";

for arg in args {
if arg == "--global" || arg == "-g" || arg == "--location=global" {
is_global = true;
break;
}
}
}

// yarn global add <dep>
ToolType::Yarn => {
is_global = args[0] == "global";
is_install_command = args[1] == "add";
}
_ => {}
};

if is_install_command && is_global {
let tool = create_tool(&tool_type).await?;

return Err(ProtoError::Message(format!(
"Global binaries must be installed with {} and {} should be added to your {}!\nLearn more: {}",
color::shell(format!("proto install-global {}", tool.get_id())),
color::path(tool.get_globals_bin_dir()?),
color::shell("PATH"),
color::url("https://moonrepo.dev/docs/proto/faq#how-can-i-install-a-global-binary-for-a-language")
)))?;
}

Ok(())
}
78 changes: 78 additions & 0 deletions crates/cli/tests/run_lang_test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
mod utils;

use starbase_sandbox::predicates::prelude::*;
use utils::*;

mod npm {
use super::*;

#[test]
fn errors_if_installing_global() {
let temp = create_empty_sandbox();

let mut cmd = create_proto_command(temp.path());
cmd.arg("install")
.arg("npm")
.arg("latest")
.assert()
.success();

let mut cmd = create_proto_command(temp.path());
let assert = cmd
.arg("run")
.arg("npm")
.arg("latest")
.args(["--", "install", "-g", "typescript"])
.assert();

assert.stderr(predicate::str::contains(
"Global binaries must be installed with proto install-global npm",
));
}
}

mod pnpm {
use super::*;

#[test]
fn errors_if_installing_global() {
let temp = create_empty_sandbox();

let mut cmd = create_proto_command(temp.path());
cmd.arg("install").arg("pnpm").assert().success();

let mut cmd = create_proto_command(temp.path());
let assert = cmd
.arg("run")
.arg("pnpm")
.args(["--", "add", "-g", "typescript"])
.assert();

assert.stderr(predicate::str::contains(
"Global binaries must be installed with proto install-global pnpm",
));
}
}

mod yarn {
use super::*;

#[test]
fn errors_if_installing_global() {
let temp = create_empty_sandbox();

let mut cmd = create_proto_command(temp.path());
cmd.arg("install").arg("yarn").assert().success();

let mut cmd = create_proto_command(temp.path());
let assert = cmd
.arg("run")
.arg("yarn")
.args(["--", "global", "add", "typescript"])
.assert();

assert.stderr(predicate::str::contains(
"Global binaries must be installed with proto install-global yarn",
));
}
}

0 comments on commit ba5e851

Please sign in to comment.