diff --git a/cli/deno_dir.rs b/cli/deno_dir.rs index e38d454e1b..5b7b65f4bc 100644 --- a/cli/deno_dir.rs +++ b/cli/deno_dir.rs @@ -156,6 +156,7 @@ impl DenoDir { specifier: &str, referrer: &str, use_cache: bool, + no_fetch: bool, ) -> impl Future { debug!( "fetch_module_meta_data. specifier {} referrer {}", @@ -184,6 +185,7 @@ impl DenoDir { module_name.as_str(), filename.as_str(), use_cache, + no_fetch, ).then(move |result| { let mut out = match result { Ok(out) => out, @@ -257,9 +259,11 @@ impl DenoDir { specifier: &str, referrer: &str, use_cache: bool, + no_fetch: bool, ) -> Result { tokio_util::block_on( - self.fetch_module_meta_data_async(specifier, referrer, use_cache), + self + .fetch_module_meta_data_async(specifier, referrer, use_cache, no_fetch), ) } @@ -364,7 +368,7 @@ impl DenoDir { impl SourceMapGetter for DenoDir { fn get_source_map(&self, script_name: &str) -> Option> { - match self.fetch_module_meta_data(script_name, ".", true) { + match self.fetch_module_meta_data(script_name, ".", true, true) { Err(_e) => None, Ok(out) => match out.maybe_source_map { None => None, @@ -392,14 +396,16 @@ fn get_source_code_async( module_name: &str, filename: &str, use_cache: bool, + no_fetch: bool, ) -> impl Future { let filename = filename.to_string(); let module_name = module_name.to_string(); let is_module_remote = is_remote(&module_name); - // We try fetch local. Two cases: - // 1. This is a remote module and we're allowed to use cached downloads. - // 2. This is a local module. - if !is_module_remote || use_cache { + // We try fetch local. Three cases: + // 1. Remote downloads are not allowed, we're only allowed to use cache. + // 2. This is a remote module and we're allowed to use cached downloads. + // 3. This is a local module. + if !is_module_remote || use_cache || no_fetch { debug!( "fetch local or reload {} is_module_remote {}", module_name, is_module_remote @@ -419,7 +425,7 @@ fn get_source_code_async( } } - // If not remote file, stop here! + // If not remote file stop here! if !is_module_remote { debug!("not remote file stop here"); return Either::A(futures::future::err(DenoError::from( @@ -430,6 +436,17 @@ fn get_source_code_async( ))); } + // If remote downloads are not allowed stop here! + if no_fetch { + debug!("remote file with no_fetch stop here"); + return Either::A(futures::future::err(DenoError::from( + std::io::Error::new( + std::io::ErrorKind::NotFound, + format!("cannot find remote file '{}' in cache", &filename), + ), + ))); + } + debug!("is remote but didn't find module"); // not cached/local, try remote. @@ -454,12 +471,14 @@ fn get_source_code( module_name: &str, filename: &str, use_cache: bool, + no_fetch: bool, ) -> DenoResult { tokio_util::block_on(get_source_code_async( deno_dir, module_name, filename, use_cache, + no_fetch, )) } @@ -1063,7 +1082,8 @@ mod tests { ); let headers_file_name = source_code_headers_filename(&filename); - let result = get_source_code(&deno_dir, module_name, &filename, true); + let result = + get_source_code(&deno_dir, module_name, &filename, true, false); assert!(result.is_ok()); let r = result.unwrap(); assert_eq!( @@ -1077,7 +1097,8 @@ mod tests { // Modify .headers.json, write using fs write and read using save_source_code_headers let _ = fs::write(&headers_file_name, "{ \"mime_type\": \"text/javascript\" }"); - let result2 = get_source_code(&deno_dir, module_name, &filename, true); + let result2 = + get_source_code(&deno_dir, module_name, &filename, true, false); assert!(result2.is_ok()); let r2 = result2.unwrap(); assert_eq!( @@ -1098,7 +1119,8 @@ mod tests { Some("application/json".to_owned()), None, ); - let result3 = get_source_code(&deno_dir, module_name, &filename, true); + let result3 = + get_source_code(&deno_dir, module_name, &filename, true, false); assert!(result3.is_ok()); let r3 = result3.unwrap(); assert_eq!( @@ -1115,7 +1137,8 @@ mod tests { ); // Don't use_cache - let result4 = get_source_code(&deno_dir, module_name, &filename, false); + let result4 = + get_source_code(&deno_dir, module_name, &filename, false, false); assert!(result4.is_ok()); let r4 = result4.unwrap(); let expected4 = @@ -1141,7 +1164,8 @@ mod tests { ); let headers_file_name = source_code_headers_filename(&filename); - let result = get_source_code(&deno_dir, module_name, &filename, true); + let result = + get_source_code(&deno_dir, module_name, &filename, true, false); assert!(result.is_ok()); let r = result.unwrap(); let expected = "export const loaded = true;\n".as_bytes(); @@ -1159,7 +1183,8 @@ mod tests { Some("text/typescript".to_owned()), None, ); - let result2 = get_source_code(&deno_dir, module_name, &filename, true); + let result2 = + get_source_code(&deno_dir, module_name, &filename, true, false); assert!(result2.is_ok()); let r2 = result2.unwrap(); let expected2 = "export const loaded = true;\n".as_bytes(); @@ -1170,7 +1195,8 @@ mod tests { assert!(fs::read_to_string(&headers_file_name).is_err()); // Don't use_cache - let result3 = get_source_code(&deno_dir, module_name, &filename, false); + let result3 = + get_source_code(&deno_dir, module_name, &filename, false, false); assert!(result3.is_ok()); let r3 = result3.unwrap(); let expected3 = "export const loaded = true;\n".as_bytes(); @@ -1211,6 +1237,7 @@ mod tests { redirect_module_name, &redirect_source_filename, true, + false, ).unwrap(); // File that requires redirection is not downloaded. assert!(fs::read_to_string(&redirect_source_filename).is_err()); @@ -1271,6 +1298,7 @@ mod tests { redirect_module_name, &redirect_source_filename, true, + false, ).unwrap(); // File that requires redirection is not downloaded. @@ -1306,6 +1334,37 @@ mod tests { }); } + #[test] + fn test_get_source_code_no_fetch() { + let (_temp_dir, deno_dir) = test_setup(); + tokio_util::init(|| { + let module_name = "http://localhost:4545/tests/002_hello.ts"; + let filename = deno_fs::normalize_path( + deno_dir + .deps_http + .join("localhost_PORT4545/tests/002_hello.ts") + .as_ref(), + ); + + // file hasn't been cached before and remote downloads are not allowed + let result = + get_source_code(&deno_dir, module_name, &filename, true, true); + assert!(result.is_err()); + let err = result.err().unwrap(); + assert_eq!(err.kind(), ErrorKind::NotFound); + + // download and cache file + let result = + get_source_code(&deno_dir, module_name, &filename, true, false); + assert!(result.is_ok()); + + // module is already cached, should be ok even with `no_fetch` + let result = + get_source_code(&deno_dir, module_name, &filename, true, true); + assert!(result.is_ok()); + }); + } + #[test] fn test_fetch_source_async_1() { use crate::tokio_util; @@ -1480,13 +1539,13 @@ mod tests { // Test failure case. let specifier = "hello.ts"; let referrer = add_root!("/baddir/badfile.ts"); - let r = deno_dir.fetch_module_meta_data(specifier, referrer, true); + let r = deno_dir.fetch_module_meta_data(specifier, referrer, true, false); assert!(r.is_err()); // Assuming cwd is the deno repo root. let specifier = "./js/main.ts"; let referrer = cwd_string.as_str(); - let r = deno_dir.fetch_module_meta_data(specifier, referrer, true); + let r = deno_dir.fetch_module_meta_data(specifier, referrer, true, false); assert!(r.is_ok()); }) } @@ -1503,13 +1562,15 @@ mod tests { // Test failure case. let specifier = "hello.ts"; let referrer = add_root!("/baddir/badfile.ts"); - let r = deno_dir.fetch_module_meta_data(specifier, referrer, false); + let r = + deno_dir.fetch_module_meta_data(specifier, referrer, false, false); assert!(r.is_err()); // Assuming cwd is the deno repo root. let specifier = "./js/main.ts"; let referrer = cwd_string.as_str(); - let r = deno_dir.fetch_module_meta_data(specifier, referrer, false); + let r = + deno_dir.fetch_module_meta_data(specifier, referrer, false, false); assert!(r.is_ok()); }) } diff --git a/cli/flags.rs b/cli/flags.rs index d66c9528cd..380c87b907 100644 --- a/cli/flags.rs +++ b/cli/flags.rs @@ -22,6 +22,7 @@ pub struct DenoFlags { pub allow_run: bool, pub allow_high_precision: bool, pub no_prompts: bool, + pub no_fetch: bool, pub v8_flags: Option>, } @@ -70,6 +71,10 @@ pub fn create_cli_app<'a, 'b>() -> App<'a, 'b> { Arg::with_name("no-prompt") .long("no-prompt") .help("Do not use prompts"), + ).arg( + Arg::with_name("no-fetch") + .long("no-fetch") + .help("Do not download remote modules"), ).arg( Arg::with_name("log-debug") .short("D") @@ -242,6 +247,9 @@ pub fn parse_flags(matches: ArgMatches) -> DenoFlags { if matches.is_present("no-prompt") { flags.no_prompts = true; } + if matches.is_present("no-fetch") { + flags.no_fetch = true; + } if matches.is_present("v8-options") { let v8_flags = svec!["deno", "--help"]; flags.v8_flags = Some(v8_flags); diff --git a/cli/ops.rs b/cli/ops.rs index b75140dcc7..b2b9b42455 100644 --- a/cli/ops.rs +++ b/cli/ops.rs @@ -442,13 +442,14 @@ fn op_fetch_module_meta_data( assert_eq!(state.dir.root.join("gen"), state.dir.gen, "Sanity check"); let use_cache = !state.flags.reload; + let no_fetch = state.flags.no_fetch; Box::new(futures::future::result(|| -> OpResult { let builder = &mut FlatBufferBuilder::new(); // TODO(ry) Use fetch_module_meta_data_async. let out = state .dir - .fetch_module_meta_data(specifier, referrer, use_cache)?; + .fetch_module_meta_data(specifier, referrer, use_cache, no_fetch)?; let data_off = builder.create_vector(out.source_code.as_slice()); let msg_args = msg::FetchModuleMetaDataResArgs { module_name: Some(builder.create_string(&out.module_name)), diff --git a/cli/worker.rs b/cli/worker.rs index 8b420cef69..317b104a0f 100644 --- a/cli/worker.rs +++ b/cli/worker.rs @@ -211,12 +211,13 @@ fn fetch_module_meta_data_and_maybe_compile_async( referrer: &str, ) -> impl Future { let use_cache = !state.flags.reload; + let no_fetch = state.flags.no_fetch; let state_ = state.clone(); let specifier = specifier.to_string(); let referrer = referrer.to_string(); state .dir - .fetch_module_meta_data_async(&specifier, &referrer, use_cache) + .fetch_module_meta_data_async(&specifier, &referrer, use_cache, no_fetch) .and_then(move |out| { if out.media_type == msg::MediaType::TypeScript && !out.has_output_code_and_source_map()