diff --git a/cli/file_fetcher.rs b/cli/file_fetcher.rs index 29fe04d8a7..99ea61795f 100644 --- a/cli/file_fetcher.rs +++ b/cli/file_fetcher.rs @@ -70,6 +70,7 @@ pub struct SourceFileFetcher { deps_cache: DiskCache, progress: Progress, source_file_cache: SourceFileCache, + cache_blacklist: Vec, use_disk_cache: bool, no_remote_fetch: bool, } @@ -79,12 +80,14 @@ impl SourceFileFetcher { deps_cache: DiskCache, progress: Progress, use_disk_cache: bool, + cache_blacklist: Vec, no_remote_fetch: bool, ) -> std::io::Result { let file_fetcher = Self { deps_cache, progress, source_file_cache: SourceFileCache::default(), + cache_blacklist, use_disk_cache, no_remote_fetch, }; @@ -308,8 +311,10 @@ impl SourceFileFetcher { return Box::new(futures::future::err(too_many_redirects())); } + let is_blacklisted = + check_cache_blacklist(module_url, self.cache_blacklist.as_ref()); // First try local cache - if use_disk_cache { + if use_disk_cache && !is_blacklisted { match self.fetch_cached_remote_source(&module_url) { Ok(Some(source_file)) => { return Box::new(futures::future::ok(source_file)); @@ -552,6 +557,26 @@ fn filter_shebang(bytes: Vec) -> Vec { } } +fn check_cache_blacklist(url: &Url, black_list: &[String]) -> bool { + let mut url_without_fragmets = url.clone(); + url_without_fragmets.set_fragment(None); + if black_list.contains(&String::from(url_without_fragmets.as_str())) { + return true; + } + let mut url_without_query_strings = url_without_fragmets; + url_without_query_strings.set_query(None); + let mut path_buf = PathBuf::from(url_without_query_strings.as_str()); + loop { + if black_list.contains(&String::from(path_buf.to_str().unwrap())) { + return true; + } + if !path_buf.pop() { + break; + } + } + false +} + #[derive(Debug, Default)] /// Header metadata associated with a particular "symbolic" source code file. /// (the associated source code file might not be cached, while remaining @@ -636,6 +661,7 @@ mod tests { DiskCache::new(&dir_path.to_path_buf().join("deps")), Progress::new(), true, + vec![], false, ) .expect("setup fail") @@ -657,6 +683,65 @@ mod tests { }; } + #[test] + fn test_cache_blacklist() { + let args = crate::flags::resolve_urls(vec![ + String::from("http://deno.land/std"), + String::from("http://github.com/example/mod.ts"), + String::from("http://fragment.com/mod.ts#fragment"), + String::from("http://query.com/mod.ts?foo=bar"), + String::from("http://queryandfragment.com/mod.ts?foo=bar#fragment"), + ]); + + let u: Url = "http://deno.land/std/fs/mod.ts".parse().unwrap(); + assert_eq!(check_cache_blacklist(&u, &args), true); + + let u: Url = "http://github.com/example/file.ts".parse().unwrap(); + assert_eq!(check_cache_blacklist(&u, &args), false); + + let u: Url = "http://github.com/example/mod.ts".parse().unwrap(); + assert_eq!(check_cache_blacklist(&u, &args), true); + + let u: Url = "http://github.com/example/mod.ts?foo=bar".parse().unwrap(); + assert_eq!(check_cache_blacklist(&u, &args), true); + + let u: Url = "http://github.com/example/mod.ts#fragment".parse().unwrap(); + assert_eq!(check_cache_blacklist(&u, &args), true); + + let u: Url = "http://fragment.com/mod.ts".parse().unwrap(); + assert_eq!(check_cache_blacklist(&u, &args), true); + + let u: Url = "http://query.com/mod.ts".parse().unwrap(); + assert_eq!(check_cache_blacklist(&u, &args), false); + + let u: Url = "http://fragment.com/mod.ts#fragment".parse().unwrap(); + assert_eq!(check_cache_blacklist(&u, &args), true); + + let u: Url = "http://query.com/mod.ts?foo=bar".parse().unwrap(); + assert_eq!(check_cache_blacklist(&u, &args), true); + + let u: Url = "http://queryandfragment.com/mod.ts".parse().unwrap(); + assert_eq!(check_cache_blacklist(&u, &args), false); + + let u: Url = "http://queryandfragment.com/mod.ts?foo=bar" + .parse() + .unwrap(); + assert_eq!(check_cache_blacklist(&u, &args), true); + + let u: Url = "http://queryandfragment.com/mod.ts#fragment" + .parse() + .unwrap(); + assert_eq!(check_cache_blacklist(&u, &args), false); + + let u: Url = "http://query.com/mod.ts?foo=bar#fragment".parse().unwrap(); + assert_eq!(check_cache_blacklist(&u, &args), true); + + let u: Url = "http://fragment.com/mod.ts?foo=bar#fragment" + .parse() + .unwrap(); + assert_eq!(check_cache_blacklist(&u, &args), true); + } + #[test] fn test_source_code_headers_get_and_save() { let (_temp_dir, fetcher) = test_setup(); diff --git a/cli/flags.rs b/cli/flags.rs index 8690ac983f..612415ec7a 100644 --- a/cli/flags.rs +++ b/cli/flags.rs @@ -11,6 +11,7 @@ use log::Level; use std; use std::str; use std::str::FromStr; +use url::Url; macro_rules! std_url { ($x:expr) => { @@ -45,6 +46,7 @@ pub struct DenoFlags { pub import_map_path: Option, pub allow_read: bool, pub read_whitelist: Vec, + pub cache_blacklist: Vec, pub allow_write: bool, pub write_whitelist: Vec, pub allow_net: bool, @@ -172,8 +174,20 @@ To get help on the another subcommands (run in this case): ).arg( Arg::with_name("reload") .short("r") + .min_values(0) + .takes_value(true) + .use_delimiter(true) + .require_equals(true) .long("reload") .help("Reload source code cache (recompile TypeScript)") + .value_name("CACHE_BLACKLIST") + .long_help("Reload source code cache (recompile TypeScript) + --reload + Reload everything + --reload=https://deno.land/std + Reload all standard modules + --reload=https://deno.land/std/fs/utils.ts,https://deno.land/std/fmt/colors.ts + Reloads specific modules") .global(true), ).arg( Arg::with_name("config") @@ -509,6 +523,23 @@ fn resolve_paths(paths: Vec) -> Vec { out } +pub fn resolve_urls(urls: Vec) -> Vec { + let mut out: Vec = vec![]; + for urlstr in urls.iter() { + let result = Url::from_str(urlstr); + if result.is_err() { + panic!("Bad Url: {}", urlstr); + } + let mut url = result.unwrap(); + url.set_fragment(None); + let mut full_url = String::from(url.as_str()); + if full_url.len() > 1 && full_url.ends_with('/') { + full_url.pop(); + } + out.push(full_url); + } + out +} /// This function expands "bare port" paths (eg. ":8080") /// into full paths with hosts. It expands to such paths /// into 3 paths with following hosts: `0.0.0.0:port`, `127.0.0.1:port` and `localhost:port`. @@ -566,7 +597,16 @@ pub fn parse_flags( flags.version = true; } if matches.is_present("reload") { - flags.reload = true; + if matches.value_of("reload").is_some() { + let cache_bl = matches.values_of("reload").unwrap(); + let raw_cache_blacklist: Vec = + cache_bl.map(std::string::ToString::to_string).collect(); + flags.cache_blacklist = resolve_urls(raw_cache_blacklist); + debug!("cache blacklist: {:#?}", &flags.cache_blacklist); + flags.reload = false; + } else { + flags.reload = true; + } } flags.config_path = matches.value_of("config").map(ToOwned::to_owned); if matches.is_present("v8-options") { diff --git a/cli/state.rs b/cli/state.rs index 70845fb8b9..950bdba703 100644 --- a/cli/state.rs +++ b/cli/state.rs @@ -225,6 +225,7 @@ impl ThreadSafeState { dir.deps_cache.clone(), progress.clone(), !flags.reload, + flags.cache_blacklist.clone(), flags.no_fetch, )?; diff --git a/website/manual.md b/website/manual.md index b56c937611..ec56123725 100644 --- a/website/manual.md +++ b/website/manual.md @@ -381,6 +381,24 @@ And if you ever want to upgrade to the latest published version: $ file_server --reload ``` +### Reload specific modules + +Sometimes we want to upgrade only some modules. You can control it by passing an +argument to a `--reload` flag. + +To reload everything + +`--reload` + +To reload all standard modules + +`--reload=https://deno.land/std` + +To reload specific modules (in this example - colors and file system utils) use +a comma to separate URLs + +`--reload=https://deno.land/std/fs/utils.ts,https://deno.land/std/fmt/colors.ts` + ### Permissions whitelist Deno also provides permissions whitelist. @@ -670,7 +688,7 @@ OPTIONS: -L, --log-level Set log level [possible values: debug, info] --no-fetch Do not download remote modules --no-prompt Do not use prompts - -r, --reload Reload source code cache (recompile TypeScript) + -r, --reload= Reload source code cache (recompile TypeScript) --seed Seed Math.random() --v8-flags= Set V8 command line options --v8-options Print V8 command line options