Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
123 changes: 118 additions & 5 deletions cli/args/flags.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2242,7 +2242,7 @@ pub fn flags_from_vec_with_initial_cwd(
"add" => add_parse(&mut flags, &mut m)?,
"audit" => audit_parse(&mut flags, &mut m)?,
"approve-scripts" => approve_scripts_parse(&mut flags, &mut m)?,
"remove" => remove_parse(&mut flags, &mut m),
"remove" => remove_parse(&mut flags, &mut m)?,
"bench" => bench_parse(&mut flags, &mut m)?,
"bundle" => bundle_parse(&mut flags, &mut m)?,
"cache" => cache_parse(&mut flags, &mut m)?,
Expand Down Expand Up @@ -2801,6 +2801,10 @@ fn remove_subcommand() -> Command {

You can remove multiple dependencies at once:
<p(245)>deno remove @std/path @std/assert</>

With the <c>--global</> flag, this is an alias for <c>deno uninstall --global</> and
removes a globally installed executable script:
<p(245)>deno remove --global file_server</>
"
),
UnstableArgsConfig::None,
Expand All @@ -2814,9 +2818,23 @@ You can remove multiple dependencies at once:
.num_args(1..)
.action(ArgAction::Append),
)
.arg(
Arg::new("root")
.long("root")
.help("Installation root")
.requires("global")
.value_hint(ValueHint::DirPath),
)
.arg(
Arg::new("global")
.long("global")
.short('g')
.help("Remove globally installed package or module")
.action(ArgAction::SetTrue),
)
.args(lock_args())
.arg(lockfile_only_arg())
.arg(package_json_arg())
.arg(lockfile_only_arg().conflicts_with("global"))
.arg(package_json_arg().conflicts_with("global"))
})
}

Expand Down Expand Up @@ -7291,13 +7309,40 @@ fn add_parse_inner(
}
}

fn remove_parse(flags: &mut Flags, matches: &mut ArgMatches) {
fn remove_parse(
flags: &mut Flags,
matches: &mut ArgMatches,
) -> clap::error::Result<()> {
lock_args_parse(flags, matches);
let mut packages = matches.remove_many::<String>("packages").unwrap();

// `deno remove --global <name>` is an alias for `deno uninstall --global
// <name>`: it removes a globally installed executable rather than a
// dependency from the configuration file, so route it through the same
// subcommand the uninstaller uses.
if matches.get_flag("global") {
let name = packages.next().unwrap();
// A global removal targets a single executable, matching
// `deno uninstall --global`, so reject any extra names.
if packages.next().is_some() {
return Err(clap::Error::raw(
clap::error::ErrorKind::ArgumentConflict,
"--global only removes a single executable, but multiple packages were provided\n",
));
}
let root = matches.remove_one::<String>("root");
flags.subcommand = DenoSubcommand::Uninstall(UninstallFlags {
kind: UninstallKind::Global(UninstallFlagsGlobal { name, root }),
});
return Ok(());
}

flags.subcommand = DenoSubcommand::Remove(RemoveFlags {
packages: matches.remove_many::<String>("packages").unwrap().collect(),
packages: packages.collect(),
lockfile_only: matches.get_flag("lockfile-only"),
package_json: matches.get_flag("package-json"),
});
Ok(())
}

fn link_parse(
Expand Down Expand Up @@ -16136,6 +16181,74 @@ mod tests {
);
}

#[test]
fn remove_global_alias_for_uninstall() {
// `deno remove --global <name>` is an alias for `deno uninstall --global
// <name>` and produces the exact same subcommand.
for global_flag in ["--global", "-g"] {
let r =
flags_from_vec(svec!["deno", "remove", global_flag, "file_server"]);
assert_eq!(
r.unwrap(),
Flags {
subcommand: DenoSubcommand::Uninstall(UninstallFlags {
kind: UninstallKind::Global(UninstallFlagsGlobal {
name: "file_server".to_string(),
root: None,
}),
}),
..Flags::default()
}
);
}

// `--root` is honored, just like `deno uninstall --global --root`.
let r = flags_from_vec(svec![
"deno",
"remove",
"-g",
"--root",
"/user/foo/bar",
"file_server"
]);
assert_eq!(
r.unwrap(),
Flags {
subcommand: DenoSubcommand::Uninstall(UninstallFlags {
kind: UninstallKind::Global(UninstallFlagsGlobal {
name: "file_server".to_string(),
root: Some("/user/foo/bar".to_string()),
}),
}),
..Flags::default()
}
);

// A global removal targets a single executable; extra names are rejected.
let r =
flags_from_vec(svec!["deno", "remove", "-g", "file_server", "chalk"]);
assert!(r.is_err());

// `--root` requires `--global`.
let r =
flags_from_vec(svec!["deno", "remove", "--root", "/tmp", "@std/path"]);
assert!(r.is_err());

// Without `--global`, removal stays a config-file dependency removal.
let r = flags_from_vec(svec!["deno", "remove", "@david/which"]);
assert_eq!(
r.unwrap(),
Flags {
subcommand: DenoSubcommand::Remove(RemoveFlags {
packages: svec!["@david/which"],
lockfile_only: false,
package_json: false,
}),
..Flags::default()
}
);
}

#[test]
fn run_with_frozen_lockfile() {
let cases = [
Expand Down
17 changes: 17 additions & 0 deletions tests/specs/install/global/remove_global_alias/__test__.jsonc
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"tempDir": true,
"steps": [
{
"args": "install --global --root ./bins --name deno-test-bin ./main.js",
"output": "install.out"
},
{
"args": "remove --global --root ./bins deno-test-bin",
"output": "remove.out"
},
{
"args": "run -A assert_removed.js",
"output": ""
}
]
}
10 changes: 10 additions & 0 deletions tests/specs/install/global/remove_global_alias/assert_removed.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// `deno remove --global` is an alias for `deno uninstall --global`, so after
// it runs the globally installed executable should be gone from the bin dir.
const binsDir = "./bins/bin";
for await (const entry of Deno.readDir(binsDir)) {
if (entry.name === "deno-test-bin" || entry.name === "deno-test-bin.cmd") {
throw new Error(
`Expected ${entry.name} to be removed by 'deno remove --global', but it still exists`,
);
}
}
2 changes: 2 additions & 0 deletions tests/specs/install/global/remove_global_alias/install.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
✅ Successfully installed deno-test-bin
[WILDCARD]
1 change: 1 addition & 0 deletions tests/specs/install/global/remove_global_alias/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
console.log("hi from deno-test-bin");
1 change: 1 addition & 0 deletions tests/specs/install/global/remove_global_alias/remove.out
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[WILDCARD]✅ Successfully uninstalled deno-test-bin
Loading