diff --git a/src/deno_dir.rs b/src/deno_dir.rs
index 9b31df6eab..08e1ac1f1d 100644
--- a/src/deno_dir.rs
+++ b/src/deno_dir.rs
@@ -1,6 +1,7 @@
// Copyright 2018 the Deno authors. All rights reserved. MIT license.
use dirs;
use errors;
+use errors::DenoError;
use errors::DenoResult;
use errors::ErrorKind;
use fs as deno_fs;
@@ -136,38 +137,79 @@ impl DenoDir {
}
// Prototype https://github.com/denoland/deno/blob/golang/deno_dir.go#L37-L73
+ /// Fetch remote source code.
fn fetch_remote_source(
self: &Self,
module_name: &str,
filename: &str,
- ) -> DenoResult<(String, msg::MediaType)> {
- let p = Path::new(filename);
- // We write a special ".mime" file into the `.deno/deps` directory along side the
- // cached file, containing just the media type.
- let mut media_type_filename = filename.to_string();
- media_type_filename.push_str(".mime");
- let mt = Path::new(&media_type_filename);
+ ) -> DenoResult> {
+ let extensions = ["", ".ts", ".js"];
+ for ext in extensions.iter() {
+ let filename = [filename, ext].concat();
+ let module_name = [module_name, ext].concat();
+ let p = Path::new(&filename);
+ // We write a special ".mime" file into the `.deno/deps` directory along side the
+ // cached file, containing just the media type.
+ let media_type_filename = [&filename, ".mime"].concat();
+ let mt = Path::new(&media_type_filename);
+ eprint!("Downloading {}...", &module_name); // no newline
+ let maybe_source = http_util::fetch_sync_string(&module_name);
+ if let Ok((source, content_type)) = maybe_source {
+ eprintln!(""); // next line
+ match p.parent() {
+ Some(ref parent) => fs::create_dir_all(parent),
+ None => Ok(()),
+ }?;
+ deno_fs::write_file(&p, source.as_bytes(), 0o666)?;
+ deno_fs::write_file(&mt, content_type.as_bytes(), 0o666)?;
+ return Ok(Some(CodeFetchOutput {
+ module_name,
+ filename: filename.clone(), // TODO: no clone after NLL rfc
+ media_type: map_content_type(&p, Some(&content_type)),
+ source_code: source,
+ maybe_output_code: None,
+ maybe_source_map: None,
+ }));
+ } else {
+ eprintln!(" NOT FOUND");
+ }
+ }
+ Ok(None)
+ }
- let src = if self.reload || !p.exists() {
- eprintln!("Downloading {}", module_name);
- let (source, content_type) = http_util::fetch_sync_string(module_name)?;
- match p.parent() {
- Some(ref parent) => fs::create_dir_all(parent),
- None => Ok(()),
- }?;
- deno_fs::write_file(&p, source.as_bytes(), 0o666)?;
- deno_fs::write_file(&mt, content_type.as_bytes(), 0o666)?;
- (source, map_content_type(&p, Some(&content_type)))
- } else {
- let source = fs::read_to_string(&p)?;
- // .mime file might not exists with bundled deps
+ /// Fetch local or cached source code.
+ fn fetch_local_source(
+ self: &Self,
+ module_name: &str,
+ filename: &str,
+ ) -> DenoResult > {
+ let extensions = ["", ".ts", ".js"];
+ for ext in extensions.iter() {
+ let filename = [filename, ext].concat();
+ let module_name = [module_name, ext].concat();
+ let p = Path::new(&filename);
+ if !p.exists() {
+ continue;
+ }
+ let media_type_filename = [&filename, ".mime"].concat();
+ let mt = Path::new(&media_type_filename);
+ let source_code = fs::read_to_string(&p)?;
+ // .mime file might not exists
+ // this is okay for local source: maybe_content_type_str will be None
let maybe_content_type_string = fs::read_to_string(&mt).ok();
// Option -> Option<&str>
let maybe_content_type_str =
maybe_content_type_string.as_ref().map(String::as_str);
- (source, map_content_type(&p, maybe_content_type_str))
- };
- Ok(src)
+ return Ok(Some(CodeFetchOutput {
+ module_name,
+ filename: filename.clone(), // TODO: no clone after NLL rfc
+ media_type: map_content_type(&p, maybe_content_type_str),
+ source_code,
+ maybe_output_code: None,
+ maybe_source_map: None,
+ }));
+ }
+ Ok(None) // cannot find locally
}
// Prototype: https://github.com/denoland/deno/blob/golang/os.go#L122-L138
@@ -177,39 +219,33 @@ impl DenoDir {
filename: &str,
) -> DenoResult {
let is_module_remote = is_remote(module_name);
- let use_extension = |ext| {
- let module_name = format!("{}{}", module_name, ext);
- let filename = format!("{}{}", filename, ext);
- let (source_code, media_type) = if is_module_remote {
- self.fetch_remote_source(&module_name, &filename)?
- } else {
- assert_eq!(
- module_name, filename,
- "if a module isn't remote, it should have the same filename"
- );
- let path = Path::new(&filename);
- (fs::read_to_string(path)?, map_content_type(path, None))
- };
- Ok(CodeFetchOutput {
- module_name: module_name.to_string(),
- filename: filename.to_string(),
- media_type,
- source_code,
- maybe_output_code: None,
- maybe_source_map: None,
- })
- };
- let default_attempt = use_extension("");
- if default_attempt.is_ok() {
- return default_attempt;
+ // We try fetch local. Two cases:
+ // 1. This is a remote module, but no reload provided
+ // 2. This is a local module
+ if !is_module_remote || !self.reload {
+ let maybe_local_source =
+ self.fetch_local_source(&module_name, &filename)?;
+ if let Some(output) = maybe_local_source {
+ return Ok(output);
+ }
}
- debug!("Trying {}.ts...", module_name);
- let ts_attempt = use_extension(".ts");
- if ts_attempt.is_ok() {
- return ts_attempt;
+ // If not remote file, stop here!
+ if !is_module_remote {
+ return Err(DenoError::from(std::io::Error::new(
+ std::io::ErrorKind::NotFound,
+ format!("cannot find local file '{}'", filename),
+ )));
}
- debug!("Trying {}.js...", module_name);
- use_extension(".js")
+ // not cached/local, try remote
+ let maybe_remote_source =
+ self.fetch_remote_source(&module_name, &filename)?;
+ if let Some(output) = maybe_remote_source {
+ return Ok(output);
+ }
+ return Err(DenoError::from(std::io::Error::new(
+ std::io::ErrorKind::NotFound,
+ format!("cannot find remote file '{}'", filename),
+ )));
}
pub fn code_fetch(
@@ -565,7 +601,72 @@ mod tests {
}
#[test]
- fn test_fetch_remote_source_1() {
+ fn test_get_source_code() {
+ use tokio_util;
+ // http_util::fetch_sync_string requires tokio
+ tokio_util::init(|| {
+ let (temp_dir, deno_dir) = test_setup();
+ let module_name = "http://localhost:4545/tests/subdir/mod2.ts";
+ let filename = deno_fs::normalize_path(
+ deno_dir
+ .deps_http
+ .join("localhost_PORT4545/tests/subdir/mod2.ts")
+ .as_ref(),
+ );
+ let mime_file_name = format!("{}.mime", &filename);
+
+ let result = deno_dir.get_source_code(module_name, &filename);
+ assert!(result.is_ok());
+ let r = result.unwrap();
+ assert_eq!(
+ &(r.source_code),
+ "export { printHello } from \"./print_hello.ts\";\n"
+ );
+ assert_eq!(&(r.media_type), &msg::MediaType::TypeScript);
+ assert_eq!(
+ fs::read_to_string(&mime_file_name).unwrap(),
+ "application/typescript"
+ );
+
+ // Modify .mime
+ let _ = fs::write(&mime_file_name, "text/javascript");
+ let result2 = deno_dir.get_source_code(module_name, &filename);
+ assert!(result2.is_ok());
+ let r2 = result2.unwrap();
+ assert_eq!(
+ &(r2.source_code),
+ "export { printHello } from \"./print_hello.ts\";\n"
+ );
+ // If get_source_code does not call remote, this should be JavaScript
+ // as we modified before! (we do not overwrite .mime due to no http fetch)
+ assert_eq!(&(r2.media_type), &msg::MediaType::JavaScript);
+ assert_eq!(
+ fs::read_to_string(&mime_file_name).unwrap(),
+ "text/javascript"
+ );
+
+ // Force self.reload
+ let deno_dir = DenoDir::new(true, Some(temp_dir.path().to_path_buf()))
+ .expect("setup fail");
+ let result3 = deno_dir.get_source_code(module_name, &filename);
+ assert!(result3.is_ok());
+ let r3 = result3.unwrap();
+ assert_eq!(
+ &(r3.source_code),
+ "export { printHello } from \"./print_hello.ts\";\n"
+ );
+ // Now the .mime file should be overwritten back to TypeScript!
+ // (due to http fetch)
+ assert_eq!(&(r3.media_type), &msg::MediaType::TypeScript);
+ assert_eq!(
+ fs::read_to_string(&mime_file_name).unwrap(),
+ "application/typescript"
+ );
+ });
+ }
+
+ #[test]
+ fn test_fetch_source_1() {
use tokio_util;
// http_util::fetch_sync_string requires tokio
tokio_util::init(|| {
@@ -582,24 +683,24 @@ mod tests {
let result = deno_dir.fetch_remote_source(module_name, &filename);
assert!(result.is_ok());
- let r = result.unwrap();
- assert_eq!(&(r.0), "export const loaded = true;\n");
- assert_eq!(&(r.1), &msg::MediaType::TypeScript);
+ let r = result.unwrap().unwrap();
+ assert_eq!(&(r.source_code), "export const loaded = true;\n");
+ assert_eq!(&(r.media_type), &msg::MediaType::TypeScript);
assert_eq!(fs::read_to_string(&mime_file_name).unwrap(), "video/mp2t");
- // Modify .mime, make sure still read from local
+ // Modify .mime, make sure read from local
let _ = fs::write(&mime_file_name, "text/javascript");
- let result2 = deno_dir.fetch_remote_source(module_name, &filename);
+ let result2 = deno_dir.fetch_local_source(module_name, &filename);
assert!(result2.is_ok());
- let r2 = result2.unwrap();
- assert_eq!(&(r2.0), "export const loaded = true;\n");
+ let r2 = result2.unwrap().unwrap();
+ assert_eq!(&(r2.source_code), "export const loaded = true;\n");
// Not MediaType::TypeScript due to .mime modification
- assert_eq!(&(r2.1), &msg::MediaType::JavaScript);
+ assert_eq!(&(r2.media_type), &msg::MediaType::JavaScript);
});
}
#[test]
- fn test_fetch_remote_source_2() {
+ fn test_fetch_source_2() {
// only local, no http_util::fetch_sync_string called
let (_temp_dir, deno_dir) = test_setup();
let cwd = std::env::current_dir().unwrap();
@@ -608,11 +709,11 @@ mod tests {
let filename =
format!("{}/tests/subdir/mt_text_typescript.t1.ts", &cwd_string);
- let result = deno_dir.fetch_remote_source(module_name, &filename);
+ let result = deno_dir.fetch_local_source(module_name, &filename);
assert!(result.is_ok());
- let r = result.unwrap();
- assert_eq!(&(r.0), "export const loaded = true;\n");
- assert_eq!(&(r.1), &msg::MediaType::TypeScript);
+ let r = result.unwrap().unwrap();
+ assert_eq!(&(r.source_code), "export const loaded = true;\n");
+ assert_eq!(&(r.media_type), &msg::MediaType::TypeScript);
}
#[test]