diff options
author | David Sherret <dsherret@users.noreply.github.com> | 2024-08-12 16:17:25 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-08-12 16:17:25 -0400 |
commit | 085058cfffa03663839b946153faf58860aed9ef (patch) | |
tree | 8d915e2f320b9a2e8bc509602230158db15b48a4 /cli | |
parent | b1036e4d9c6ce565ed50f88034b598a317f69fbd (diff) |
feat: deno remove (#24952)
Co-authored-by: Satya Rohith <me@satyarohith.com>
Diffstat (limited to 'cli')
-rw-r--r-- | cli/args/flags.rs | 68 | ||||
-rw-r--r-- | cli/factory.rs | 2 | ||||
-rw-r--r-- | cli/main.rs | 3 | ||||
-rw-r--r-- | cli/tools/registry/mod.rs | 1 | ||||
-rw-r--r-- | cli/tools/registry/pm.rs | 85 |
5 files changed, 155 insertions, 4 deletions
diff --git a/cli/args/flags.rs b/cli/args/flags.rs index c6bb90430..b9908f413 100644 --- a/cli/args/flags.rs +++ b/cli/args/flags.rs @@ -84,6 +84,11 @@ pub struct AddFlags { } #[derive(Clone, Debug, Default, Eq, PartialEq)] +pub struct RemoveFlags { + pub packages: Vec<String>, +} + +#[derive(Clone, Debug, Default, Eq, PartialEq)] pub struct BenchFlags { pub files: FileFlags, pub filter: Option<String>, @@ -428,6 +433,7 @@ pub struct HelpFlags { #[derive(Clone, Debug, Eq, PartialEq)] pub enum DenoSubcommand { Add(AddFlags), + Remove(RemoveFlags), Bench(BenchFlags), Bundle(BundleFlags), Cache(CacheFlags), @@ -1216,6 +1222,7 @@ pub fn flags_from_vec(args: Vec<OsString>) -> clap::error::Result<Flags> { if let Some((subcommand, mut m)) = matches.remove_subcommand() { match subcommand.as_str() { "add" => add_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), @@ -1442,6 +1449,7 @@ pub fn clap_root() -> Command { .defer(|cmd| { let cmd = cmd .subcommand(add_subcommand()) + .subcommand(remove_subcommand()) .subcommand(bench_subcommand()) .subcommand(bundle_subcommand()) .subcommand(cache_subcommand()) @@ -1515,6 +1523,31 @@ You can add multiple dependencies at once: }) } +fn remove_subcommand() -> Command { + Command::new("remove") + .alias("rm") + .about("Remove dependencies") + .long_about( + "Remove dependencies from the configuration file. + + deno remove @std/path + +You can remove multiple dependencies at once: + + deno remove @std/path @std/assert +", + ) + .defer(|cmd| { + cmd.arg( + Arg::new("packages") + .help("List of packages to remove") + .required(true) + .num_args(1..) + .action(ArgAction::Append), + ) + }) +} + fn bench_subcommand() -> Command { Command::new("bench") .about( @@ -3726,6 +3759,12 @@ fn add_parse_inner( AddFlags { packages } } +fn remove_parse(flags: &mut Flags, matches: &mut ArgMatches) { + flags.subcommand = DenoSubcommand::Remove(RemoveFlags { + packages: matches.remove_many::<String>("packages").unwrap().collect(), + }); +} + fn bench_parse(flags: &mut Flags, matches: &mut ArgMatches) { flags.type_check_mode = TypeCheckMode::Local; @@ -10248,6 +10287,35 @@ mod tests { } #[test] + fn remove_subcommand() { + let r = flags_from_vec(svec!["deno", "remove"]); + r.unwrap_err(); + + let r = flags_from_vec(svec!["deno", "remove", "@david/which"]); + assert_eq!( + r.unwrap(), + Flags { + subcommand: DenoSubcommand::Remove(RemoveFlags { + packages: svec!["@david/which"], + }), + ..Flags::default() + } + ); + + let r = + flags_from_vec(svec!["deno", "remove", "@david/which", "@luca/hello"]); + assert_eq!( + r.unwrap(), + Flags { + subcommand: DenoSubcommand::Remove(RemoveFlags { + packages: svec!["@david/which", "@luca/hello"], + }), + ..Flags::default() + } + ); + } + + #[test] fn run_with_frozen_lockfile() { let cases = [ (Some("--frozen"), true), diff --git a/cli/factory.rs b/cli/factory.rs index ed288b22f..942aefd25 100644 --- a/cli/factory.rs +++ b/cli/factory.rs @@ -355,7 +355,7 @@ impl CliFactory { let fs = self.fs(); let cli_options = self.cli_options()?; // For `deno install` we want to force the managed resolver so it can set up `node_modules/` directory. - create_cli_npm_resolver(if cli_options.use_byonm() && !matches!(cli_options.sub_command(), DenoSubcommand::Install(_) | DenoSubcommand::Add(_)) { + create_cli_npm_resolver(if cli_options.use_byonm() && !matches!(cli_options.sub_command(), DenoSubcommand::Install(_) | DenoSubcommand::Add(_) | DenoSubcommand::Remove(_)) { CliNpmResolverCreateOptions::Byonm(CliNpmResolverByonmCreateOptions { fs: fs.clone(), root_node_modules_dir: Some(match cli_options.node_modules_dir_path() { diff --git a/cli/main.rs b/cli/main.rs index 4955b79d0..1b2640758 100644 --- a/cli/main.rs +++ b/cli/main.rs @@ -100,6 +100,9 @@ async fn run_subcommand(flags: Arc<Flags>) -> Result<i32, AnyError> { DenoSubcommand::Add(add_flags) => spawn_subcommand(async { tools::registry::add(flags, add_flags, tools::registry::AddCommandName::Add).await }), + DenoSubcommand::Remove(remove_flags) => spawn_subcommand(async { + tools::registry::remove(flags, remove_flags).await + }), DenoSubcommand::Bench(bench_flags) => spawn_subcommand(async { if bench_flags.watch.is_some() { tools::bench::run_benchmarks_with_watch(flags, bench_flags).await diff --git a/cli/tools/registry/mod.rs b/cli/tools/registry/mod.rs index b3bed7721..34e803c73 100644 --- a/cli/tools/registry/mod.rs +++ b/cli/tools/registry/mod.rs @@ -64,6 +64,7 @@ mod unfurl; use auth::get_auth_method; use auth::AuthMethod; pub use pm::add; +pub use pm::remove; pub use pm::AddCommandName; use publish_order::PublishOrderGraph; use unfurl::SpecifierUnfurler; diff --git a/cli/tools/registry/pm.rs b/cli/tools/registry/pm.rs index 86596df9d..90238b890 100644 --- a/cli/tools/registry/pm.rs +++ b/cli/tools/registry/pm.rs @@ -1,6 +1,7 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. use std::borrow::Cow; +use std::path::Path; use std::path::PathBuf; use std::sync::Arc; @@ -23,6 +24,7 @@ use jsonc_parser::ast::Value; use crate::args::AddFlags; use crate::args::CacheSetting; use crate::args::Flags; +use crate::args::RemoveFlags; use crate::factory::CliFactory; use crate::file_fetcher::FileFetcher; use crate::jsr::JsrFetchResolver; @@ -337,9 +339,7 @@ pub async fn add( // make a new CliFactory to pick up the updated config file let cli_factory = CliFactory::from_flags(flags); // cache deps - if cli_factory.cli_options()?.enable_future_features() { - crate::module_loader::load_top_level_deps(&cli_factory).await?; - } + crate::module_loader::load_top_level_deps(&cli_factory).await?; Ok(()) } @@ -513,6 +513,85 @@ fn generate_imports(packages_to_version: Vec<(String, String)>) -> String { contents.join("\n") } +fn remove_from_config( + config_path: &Path, + keys: &[&'static str], + packages_to_remove: &[String], + removed_packages: &mut Vec<String>, + fmt_options: &FmtOptionsConfig, +) -> Result<(), AnyError> { + let mut json: serde_json::Value = + serde_json::from_slice(&std::fs::read(config_path)?)?; + for key in keys { + let Some(obj) = json.get_mut(*key).and_then(|v| v.as_object_mut()) else { + continue; + }; + for package in packages_to_remove { + if obj.shift_remove(package).is_some() { + removed_packages.push(package.clone()); + } + } + } + + let config = serde_json::to_string_pretty(&json)?; + let config = + crate::tools::fmt::format_json(config_path, &config, fmt_options) + .ok() + .flatten() + .unwrap_or(config); + + std::fs::write(config_path, config) + .context("Failed to update configuration file")?; + + Ok(()) +} + +pub async fn remove( + flags: Arc<Flags>, + remove_flags: RemoveFlags, +) -> Result<(), AnyError> { + let (config_file, factory) = DenoOrPackageJson::from_flags(flags.clone())?; + let options = factory.cli_options()?; + let start_dir = &options.start_dir; + let fmt_config_options = config_file.fmt_options(); + + let mut removed_packages = Vec::new(); + + if let Some(deno_json) = start_dir.maybe_deno_json() { + remove_from_config( + &deno_json.specifier.to_file_path().unwrap(), + &["imports"], + &remove_flags.packages, + &mut removed_packages, + &fmt_config_options, + )?; + } + + if let Some(pkg_json) = start_dir.maybe_pkg_json() { + remove_from_config( + &pkg_json.path, + &["dependencies", "devDependencies"], + &remove_flags.packages, + &mut removed_packages, + &fmt_config_options, + )?; + } + + if removed_packages.is_empty() { + log::info!("No packages were removed"); + } else { + for package in &removed_packages { + log::info!("Removed {}", crate::colors::green(package)); + } + // Update deno.lock + node_resolver::PackageJsonThreadLocalCache::clear(); + let cli_factory = CliFactory::from_flags(flags); + crate::module_loader::load_top_level_deps(&cli_factory).await?; + } + + Ok(()) +} + fn update_config_file_content( obj: jsonc_parser::ast::Object, config_file_contents: &str, |