Repository: denoland/deno
Pull request: feat: deno clean

Closes https://github.com/denoland/deno/issues/3437 (we can follow up with more features later)

Co-authored-by: Satya Rohith <me@satyarohith.com>

Changed files:
filename: cli/args/flags.rs
patch: @@ -417,6 +417,7 @@ pub enum DenoSubcommand {
   Bundle(BundleFlags),
   Cache(CacheFlags),
   Check(CheckFlags),
+  Clean,
   Compile(CompileFlags),
   Completions(CompletionsFlags),
   Coverage(CoverageFlags),
@@ -1192,6 +1193,7 @@ pub fn flags_from_vec(args: Vec<OsString>) -> clap::error::Result<Flags> {
       "bundle" => bundle_parse(&mut flags, &mut m),
       "cache" => cache_parse(&mut flags, &mut m),
       "check" => check_parse(&mut flags, &mut m),
+      "clean" => clean_parse(&mut flags, &mut m),
       "compile" => compile_parse(&mut flags, &mut m),
       "completions" => completions_parse(&mut flags, &mut m, app),
       "coverage" => coverage_parse(&mut flags, &mut m),
@@ -1359,6 +1361,7 @@ fn clap_root() -> Command {
         .subcommand(bundle_subcommand())
         .subcommand(cache_subcommand())
         .subcommand(check_subcommand())
+        .subcommand(clean_subcommand())
         .subcommand(compile_subcommand())
         .subcommand(completions_subcommand())
         .subcommand(coverage_subcommand())
@@ -1533,6 +1536,10 @@ Future runs of this module will trigger no downloads or compilation unless
     })
 }
 
+fn clean_subcommand() -> Command {
+  Command::new("clean").about("Remove the cache directory ($DENO_DIR)")
+}
+
 fn check_subcommand() -> Command {
   Command::new("check")
       .about("Type-check the dependencies")
@@ -3891,6 +3898,10 @@ fn check_parse(flags: &mut Flags, matches: &mut ArgMatches) {
   flags.subcommand = DenoSubcommand::Check(CheckFlags { files });
 }
 
+fn clean_parse(flags: &mut Flags, _matches: &mut ArgMatches) {
+  flags.subcommand = DenoSubcommand::Clean;
+}
+
 fn compile_parse(flags: &mut Flags, matches: &mut ArgMatches) {
   flags.type_check_mode = TypeCheckMode::Local;
   runtime_args_parse(flags, matches, true, false);

filename: cli/main.rs
patch: @@ -32,6 +32,7 @@ use crate::args::flags_from_vec;
 use crate::args::DenoSubcommand;
 use crate::args::Flags;
 use crate::args::DENO_FUTURE;
+use crate::cache::DenoDir;
 use crate::graph_container::ModuleGraphContainer;
 use crate::util::display;
 use crate::util::v8::get_v8_flags_from_env;
@@ -133,6 +134,14 @@ async fn run_subcommand(flags: Arc<Flags>) -> Result<i32, AnyError> {
         .load_and_type_check_files(&check_flags.files)
         .await
     }),
+    DenoSubcommand::Clean => spawn_subcommand(async move {
+      let deno_dir = DenoDir::new(None)?;
+      if deno_dir.root.exists() {
+        std::fs::remove_dir_all(&deno_dir.root)?;
+        log::info!("{} {}", colors::green("Removed"), deno_dir.root.display());
+      }
+      Ok::<(), std::io::Error>(())
+    }),
     DenoSubcommand::Compile(compile_flags) => spawn_subcommand(async {
       tools::compile::compile(flags, compile_flags).await
     }),

filename: tests/specs/clean/general/__test__.jsonc
patch: @@ -0,0 +1,37 @@
+{
+  "tempDir": true,
+  "envs": {
+    "DENO_DIR": "$PWD/deno_dir"
+  },
+  "steps": [{
+    // create deno dir
+    "args": "run http://localhost:4545/echo.ts hi",
+    "output": "[WILDCARD]"
+  }, {
+    "args": [
+      "eval",
+      "console.log(Deno.statSync('./deno_dir') != null)"
+    ],
+    "output": "true\n"
+  }, {
+    "args": "clean",
+    "output": "Removed [WILDLINE]/deno_dir\n"
+  }, {
+    "envs": {
+      // use a new dir to avoid creating the old one
+      "DENO_DIR": "$PWD/other"
+    },
+    "args": "clean",
+    "output": ""
+  }, {
+    "envs": {
+      // use a new dir to avoid creating the old one
+      "DENO_DIR": "$PWD/other"
+    },
+    "args": [
+      "eval",
+      "try { Deno.statSync('./deno_dir') } catch (_) { console.log(true) }"
+    ],
+    "output": "true\n"
+  }]
+}