diff options
Diffstat (limited to 'src/deno_dir.rs')
-rw-r--r-- | src/deno_dir.rs | 1369 |
1 files changed, 0 insertions, 1369 deletions
diff --git a/src/deno_dir.rs b/src/deno_dir.rs deleted file mode 100644 index 829af5aa2..000000000 --- a/src/deno_dir.rs +++ /dev/null @@ -1,1369 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -use crate::compiler::ModuleMetaData; -use crate::errors; -use crate::errors::DenoError; -use crate::errors::DenoResult; -use crate::errors::ErrorKind; -use crate::fs as deno_fs; -use crate::http_util; -use crate::js_errors::SourceMapGetter; -use crate::msg; -use crate::version; - -use dirs; -use ring; -use std; -use std::fmt::Write; -use std::fs; -use std::path::Path; -use std::path::PathBuf; -use std::result::Result; -use std::str; -use url; -use url::Url; - -/// Gets corresponding MediaType given extension -fn extmap(ext: &str) -> msg::MediaType { - match ext { - "ts" => msg::MediaType::TypeScript, - "js" => msg::MediaType::JavaScript, - "json" => msg::MediaType::Json, - _ => msg::MediaType::Unknown, - } -} - -pub struct DenoDir { - // Example: /Users/rld/.deno/ - pub root: PathBuf, - // In the Go code this was called SrcDir. - // This is where we cache http resources. Example: - // /Users/rld/.deno/deps/github.com/ry/blah.js - pub gen: PathBuf, - // In the Go code this was called CacheDir. - // This is where we cache compilation outputs. Example: - // /Users/rld/.deno/gen/f39a473452321cacd7c346a870efb0e3e1264b43.js - pub deps: PathBuf, - // This splits to http and https deps - pub deps_http: PathBuf, - pub deps_https: PathBuf, - // If remote resources should be reloaded. - reload: bool, - // recompile the typescript files. - // if true, not load cache files - // else, load cache files - recompile: bool, -} - -impl DenoDir { - // Must be called before using any function from this module. - // https://github.com/denoland/deno/blob/golang/deno_dir.go#L99-L111 - pub fn new( - reload: bool, - recompile: bool, - custom_root: Option<PathBuf>, - ) -> std::io::Result<Self> { - // Only setup once. - let home_dir = dirs::home_dir().expect("Could not get home directory."); - let fallback = home_dir.join(".deno"); - // We use the OS cache dir because all files deno writes are cache files - // Once that changes we need to start using different roots if DENO_DIR - // is not set, and keep a single one if it is. - let default = dirs::cache_dir() - .map(|d| d.join("deno")) - .unwrap_or(fallback); - - let root: PathBuf = custom_root.unwrap_or(default); - let gen = root.as_path().join("gen"); - let deps = root.as_path().join("deps"); - let deps_http = deps.join("http"); - let deps_https = deps.join("https"); - - let deno_dir = Self { - root, - gen, - deps, - deps_http, - deps_https, - reload, - recompile, - }; - - // TODO Lazily create these directories. - deno_fs::mkdir(deno_dir.gen.as_ref(), 0o755, true)?; - deno_fs::mkdir(deno_dir.deps.as_ref(), 0o755, true)?; - deno_fs::mkdir(deno_dir.deps_http.as_ref(), 0o755, true)?; - deno_fs::mkdir(deno_dir.deps_https.as_ref(), 0o755, true)?; - - debug!("root {}", deno_dir.root.display()); - debug!("gen {}", deno_dir.gen.display()); - debug!("deps {}", deno_dir.deps.display()); - debug!("deps_http {}", deno_dir.deps_http.display()); - debug!("deps_https {}", deno_dir.deps_https.display()); - - Ok(deno_dir) - } - - // https://github.com/denoland/deno/blob/golang/deno_dir.go#L32-L35 - pub fn cache_path( - self: &Self, - filename: &str, - source_code: &[u8], - ) -> (PathBuf, PathBuf) { - let cache_key = source_code_hash(filename, source_code, version::DENO); - ( - self.gen.join(cache_key.to_string() + ".js"), - self.gen.join(cache_key.to_string() + ".js.map"), - ) - } - - fn load_cache( - self: &Self, - filename: &str, - source_code: &[u8], - ) -> Result<(Vec<u8>, Vec<u8>), std::io::Error> { - let (output_code, source_map) = self.cache_path(filename, source_code); - debug!( - "load_cache code: {} map: {}", - output_code.display(), - source_map.display() - ); - let read_output_code = fs::read(&output_code)?; - let read_source_map = fs::read(&source_map)?; - Ok((read_output_code, read_source_map)) - } - - pub fn code_cache( - self: &Self, - module_meta_data: &ModuleMetaData, - ) -> std::io::Result<()> { - let (cache_path, source_map_path) = self - .cache_path(&module_meta_data.filename, &module_meta_data.source_code); - // TODO(ry) This is a race condition w.r.t to exists() -- probably should - // create the file in exclusive mode. A worry is what might happen is there - // are two processes and one reads the cache file while the other is in the - // midst of writing it. - if cache_path.exists() && source_map_path.exists() { - Ok(()) - } else { - match &module_meta_data.maybe_output_code { - Some(output_code) => fs::write(cache_path, output_code), - _ => Ok(()), - }?; - match &module_meta_data.maybe_source_map { - Some(source_map) => fs::write(source_map_path, source_map), - _ => Ok(()), - }?; - Ok(()) - } - } - - // 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<Option<ModuleMetaData>> { - 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, 0o666)?; - // Remove possibly existing stale .mime file - // may not exist. DON'T unwrap - let _ = std::fs::remove_file(&media_type_filename); - // Create .mime file only when content type different from extension - let resolved_content_type = map_content_type(&p, Some(&content_type)); - let ext = p - .extension() - .map(|x| x.to_str().unwrap_or("")) - .unwrap_or(""); - let media_type = extmap(&ext); - if media_type == msg::MediaType::Unknown - || media_type != resolved_content_type - { - deno_fs::write_file(&mt, content_type.as_bytes(), 0o666)? - } - return Ok(Some(ModuleMetaData { - module_name: module_name.to_string(), - filename: filename.to_string(), - media_type: map_content_type(&p, Some(&content_type)), - source_code: source.as_bytes().to_owned(), - maybe_output_code_filename: None, - maybe_output_code: None, - maybe_source_map_filename: None, - maybe_source_map: None, - })); - } else { - eprintln!(" NOT FOUND"); - } - Ok(None) - } - - /// Fetch local or cached source code. - fn fetch_local_source( - self: &Self, - module_name: &str, - filename: &str, - ) -> DenoResult<Option<ModuleMetaData>> { - let p = Path::new(&filename); - let media_type_filename = [&filename, ".mime"].concat(); - let mt = Path::new(&media_type_filename); - let source_code = match fs::read(p) { - Err(e) => { - if e.kind() == std::io::ErrorKind::NotFound { - return Ok(None); - } else { - return Err(e.into()); - } - } - Ok(c) => c, - }; - // .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<String> -> Option<&str> - let maybe_content_type_str = - maybe_content_type_string.as_ref().map(String::as_str); - Ok(Some(ModuleMetaData { - module_name: module_name.to_string(), - filename: filename.to_string(), - media_type: map_content_type(&p, maybe_content_type_str), - source_code, - maybe_output_code_filename: None, - maybe_output_code: None, - maybe_source_map_filename: None, - maybe_source_map: None, - })) - } - - // Prototype: https://github.com/denoland/deno/blob/golang/os.go#L122-L138 - fn get_source_code( - self: &Self, - module_name: &str, - filename: &str, - ) -> DenoResult<ModuleMetaData> { - let is_module_remote = is_remote(module_name); - // 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 { - debug!( - "fetch local or reload {} is_module_remote {}", - module_name, is_module_remote - ); - match self.fetch_local_source(&module_name, &filename)? { - Some(output) => { - debug!("found local source "); - return Ok(output); - } - None => { - debug!("fetch_local_source returned None"); - } - } - } - - // If not remote file, stop here! - if !is_module_remote { - debug!("not remote file stop here"); - return Err(DenoError::from(std::io::Error::new( - std::io::ErrorKind::NotFound, - format!("cannot find local file '{}'", filename), - ))); - } - - debug!("is remote but didn't find module"); - - // 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); - } - Err(DenoError::from(std::io::Error::new( - std::io::ErrorKind::NotFound, - format!("cannot find remote file '{}'", filename), - ))) - } - - pub fn fetch_module_meta_data( - self: &Self, - specifier: &str, - referrer: &str, - ) -> Result<ModuleMetaData, errors::DenoError> { - debug!( - "fetch_module_meta_data. specifier {} referrer {}", - specifier, referrer - ); - - let (module_name, filename) = self.resolve_module(specifier, referrer)?; - - let result = self.get_source_code(module_name.as_str(), filename.as_str()); - let mut out = match result { - Ok(out) => out, - Err(err) => { - if err.kind() == ErrorKind::NotFound { - // For NotFound, change the message to something better. - return Err(errors::new( - ErrorKind::NotFound, - format!( - "Cannot resolve module \"{}\" from \"{}\"", - specifier, referrer - ), - )); - } else { - return Err(err); - } - } - }; - - if out.source_code.starts_with("#!".as_bytes()) { - out.source_code = filter_shebang(out.source_code); - } - - if out.media_type != msg::MediaType::TypeScript { - return Ok(out); - } - - let (output_code_filename, output_source_map_filename) = - self.cache_path(&out.filename, &out.source_code); - let mut maybe_output_code = None; - let mut maybe_source_map = None; - - if !self.recompile { - let result = self.load_cache(out.filename.as_str(), &out.source_code); - match result { - Err(err) => { - if err.kind() == std::io::ErrorKind::NotFound { - return Ok(out); - } else { - return Err(err.into()); - } - } - Ok((output_code, source_map)) => { - maybe_output_code = Some(output_code); - maybe_source_map = Some(source_map); - } - } - } - - Ok(ModuleMetaData { - module_name: out.module_name, - filename: out.filename, - media_type: out.media_type, - source_code: out.source_code, - maybe_output_code_filename: output_code_filename - .to_str() - .map(String::from), - maybe_output_code, - maybe_source_map_filename: output_source_map_filename - .to_str() - .map(String::from), - maybe_source_map, - }) - } - - // Prototype: https://github.com/denoland/deno/blob/golang/os.go#L56-L68 - fn src_file_to_url(self: &Self, filename: &str) -> String { - let filename_path = Path::new(filename); - if filename_path.starts_with(&self.deps) { - let (rest, prefix) = if filename_path.starts_with(&self.deps_https) { - let rest = filename_path.strip_prefix(&self.deps_https).unwrap(); - let prefix = "https://".to_string(); - (rest, prefix) - } else if filename_path.starts_with(&self.deps_http) { - let rest = filename_path.strip_prefix(&self.deps_http).unwrap(); - let prefix = "http://".to_string(); - (rest, prefix) - } else { - // TODO(kevinkassimo): change this to support other protocols than http - unimplemented!() - }; - // Windows doesn't support ":" in filenames, so we represent port using a - // special string. - // TODO(ry) This current implementation will break on a URL that has - // the default port but contains "_PORT" in the path. - let rest = rest.to_str().unwrap().replacen("_PORT", ":", 1); - prefix + &rest - } else { - String::from(filename) - } - } - - /// Returns (module name, local filename) - pub fn resolve_module_url( - self: &Self, - specifier: &str, - referrer: &str, - ) -> Result<Url, url::ParseError> { - let specifier = self.src_file_to_url(specifier); - let mut referrer = self.src_file_to_url(referrer); - - debug!( - "resolve_module specifier {} referrer {}", - specifier, referrer - ); - - if referrer.starts_with('.') { - let cwd = std::env::current_dir().unwrap(); - let referrer_path = cwd.join(referrer); - referrer = referrer_path.to_str().unwrap().to_string() + "/"; - } - - let j = if is_remote(&specifier) || Path::new(&specifier).is_absolute() { - parse_local_or_remote(&specifier)? - } else if referrer.ends_with('/') { - let r = Url::from_directory_path(&referrer); - // TODO(ry) Properly handle error. - if r.is_err() { - error!("Url::from_directory_path error {}", referrer); - } - let base = r.unwrap(); - base.join(specifier.as_ref())? - } else { - let base = parse_local_or_remote(&referrer)?; - base.join(specifier.as_ref())? - }; - Ok(j) - } - - /// Returns (module name, local filename) - pub fn resolve_module( - self: &Self, - specifier: &str, - referrer: &str, - ) -> Result<(String, String), url::ParseError> { - let j = self.resolve_module_url(specifier, referrer)?; - - let module_name = j.to_string(); - let filename; - match j.scheme() { - "file" => { - filename = deno_fs::normalize_path(j.to_file_path().unwrap().as_ref()); - } - "https" => { - filename = deno_fs::normalize_path( - get_cache_filename(self.deps_https.as_path(), &j).as_ref(), - ) - } - "http" => { - filename = deno_fs::normalize_path( - get_cache_filename(self.deps_http.as_path(), &j).as_ref(), - ) - } - // TODO(kevinkassimo): change this to support other protocols than http - _ => unimplemented!(), - } - - debug!("module_name: {}, filename: {}", module_name, filename); - Ok((module_name, filename)) - } -} - -impl SourceMapGetter for DenoDir { - fn get_source_map(&self, script_name: &str) -> Option<Vec<u8>> { - match self.fetch_module_meta_data(script_name, ".") { - Err(_e) => None, - Ok(out) => match out.maybe_source_map { - None => None, - Some(source_map) => Some(source_map), - }, - } - } -} - -fn get_cache_filename(basedir: &Path, url: &Url) -> PathBuf { - let host = url.host_str().unwrap(); - let host_port = match url.port() { - // Windows doesn't support ":" in filenames, so we represent port using a - // special string. - Some(port) => format!("{}_PORT{}", host, port), - None => host.to_string(), - }; - - let mut out = basedir.to_path_buf(); - out.push(host_port); - for path_seg in url.path_segments().unwrap() { - out.push(path_seg); - } - out -} - -fn source_code_hash( - filename: &str, - source_code: &[u8], - version: &str, -) -> String { - let mut ctx = ring::digest::Context::new(&ring::digest::SHA1); - ctx.update(version.as_bytes()); - ctx.update(filename.as_bytes()); - ctx.update(source_code); - let digest = ctx.finish(); - let mut out = String::new(); - // TODO There must be a better way to do this... - for byte in digest.as_ref() { - write!(&mut out, "{:02x}", byte).unwrap(); - } - out -} - -fn is_remote(module_name: &str) -> bool { - module_name.starts_with("http://") || module_name.starts_with("https://") -} - -fn parse_local_or_remote(p: &str) -> Result<url::Url, url::ParseError> { - if is_remote(p) || p.starts_with("file:") { - Url::parse(p) - } else { - Url::from_file_path(p).map_err(|_err| url::ParseError::IdnaError) - } -} - -fn map_file_extension(path: &Path) -> msg::MediaType { - match path.extension() { - None => msg::MediaType::Unknown, - Some(os_str) => match os_str.to_str() { - Some("ts") => msg::MediaType::TypeScript, - Some("js") => msg::MediaType::JavaScript, - Some("json") => msg::MediaType::Json, - _ => msg::MediaType::Unknown, - }, - } -} - -// convert a ContentType string into a enumerated MediaType -fn map_content_type(path: &Path, content_type: Option<&str>) -> msg::MediaType { - match content_type { - Some(content_type) => { - // sometimes there is additional data after the media type in - // Content-Type so we have to do a bit of manipulation so we are only - // dealing with the actual media type - let ct_vector: Vec<&str> = content_type.split(';').collect(); - let ct: &str = ct_vector.first().unwrap(); - match ct.to_lowercase().as_ref() { - "application/typescript" - | "text/typescript" - | "video/vnd.dlna.mpeg-tts" - | "video/mp2t" - | "application/x-typescript" => msg::MediaType::TypeScript, - "application/javascript" - | "text/javascript" - | "application/ecmascript" - | "text/ecmascript" - | "application/x-javascript" => msg::MediaType::JavaScript, - "application/json" | "text/json" => msg::MediaType::Json, - "text/plain" => map_file_extension(path), - _ => { - debug!("unknown content type: {}", content_type); - msg::MediaType::Unknown - } - } - } - None => map_file_extension(path), - } -} - -fn filter_shebang(bytes: Vec<u8>) -> Vec<u8> { - let string = str::from_utf8(&bytes).unwrap(); - if let Some(i) = string.find('\n') { - let (_, rest) = string.split_at(i); - rest.as_bytes().to_owned() - } else { - Vec::new() - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::tokio_util; - use tempfile::TempDir; - - fn test_setup(reload: bool, recompile: bool) -> (TempDir, DenoDir) { - let temp_dir = TempDir::new().expect("tempdir fail"); - let deno_dir = - DenoDir::new(reload, recompile, Some(temp_dir.path().to_path_buf())) - .expect("setup fail"); - (temp_dir, deno_dir) - } - - // The `add_root` macro prepends "C:" to a string if on windows; on posix - // systems it returns the input string untouched. This is necessary because - // `Url::from_file_path()` fails if the input path isn't an absolute path. - macro_rules! add_root { - ($path:expr) => { - if cfg!(target_os = "windows") { - concat!("C:", $path) - } else { - $path - } - }; - } - - macro_rules! file_url { - ($path:expr) => { - if cfg!(target_os = "windows") { - concat!("file:///C:", $path) - } else { - concat!("file://", $path) - } - }; - } - - #[test] - fn test_get_cache_filename() { - let url = Url::parse("http://example.com:1234/path/to/file.ts").unwrap(); - let basedir = Path::new("/cache/dir/"); - let cache_file = get_cache_filename(&basedir, &url); - assert_eq!( - cache_file, - Path::new("/cache/dir/example.com_PORT1234/path/to/file.ts") - ); - } - - #[test] - fn test_cache_path() { - let (temp_dir, deno_dir) = test_setup(false, false); - let filename = "hello.js"; - let source_code = "1+2".as_bytes(); - let hash = source_code_hash(filename, source_code, version::DENO); - assert_eq!( - ( - temp_dir.path().join(format!("gen/{}.js", hash)), - temp_dir.path().join(format!("gen/{}.js.map", hash)) - ), - deno_dir.cache_path(filename, source_code) - ); - } - - #[test] - fn test_code_cache() { - let (_temp_dir, deno_dir) = test_setup(false, false); - - let filename = "hello.js"; - let source_code = "1+2".as_bytes(); - let output_code = "1+2 // output code".as_bytes(); - let source_map = "{}".as_bytes(); - let hash = source_code_hash(filename, source_code, version::DENO); - let (cache_path, source_map_path) = - deno_dir.cache_path(filename, source_code); - assert!(cache_path.ends_with(format!("gen/{}.js", hash))); - assert!(source_map_path.ends_with(format!("gen/{}.js.map", hash))); - - let out = ModuleMetaData { - filename: filename.to_owned(), - source_code: source_code.to_owned(), - module_name: "hello.js".to_owned(), - media_type: msg::MediaType::TypeScript, - maybe_output_code: Some(output_code.to_owned()), - maybe_output_code_filename: None, - maybe_source_map: Some(source_map.to_owned()), - maybe_source_map_filename: None, - }; - - let r = deno_dir.code_cache(&out); - r.expect("code_cache error"); - assert!(cache_path.exists()); - assert_eq!(output_code.to_owned(), fs::read(&cache_path).unwrap()); - } - - #[test] - fn test_source_code_hash() { - assert_eq!( - "7e44de2ed9e0065da09d835b76b8d70be503d276", - source_code_hash("hello.ts", "1+2".as_bytes(), "0.2.11") - ); - // Different source_code should result in different hash. - assert_eq!( - "57033366cf9db1ef93deca258cdbcd9ef5f4bde1", - source_code_hash("hello.ts", "1".as_bytes(), "0.2.11") - ); - // Different filename should result in different hash. - assert_eq!( - "19657f90b5b0540f87679e2fb362e7bd62b644b0", - source_code_hash("hi.ts", "1+2".as_bytes(), "0.2.11") - ); - // Different version should result in different hash. - assert_eq!( - "e2b4b7162975a02bf2770f16836eb21d5bcb8be1", - source_code_hash("hi.ts", "1+2".as_bytes(), "0.2.0") - ); - } - - #[test] - fn test_get_source_code_1() { - let (temp_dir, deno_dir) = test_setup(false, false); - // http_util::fetch_sync_string requires tokio - tokio_util::init(|| { - 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); - println!("module_name {} filename {}", module_name, filename); - assert!(result.is_ok()); - let r = result.unwrap(); - assert_eq!( - r.source_code, - "export { printHello } from \"./print_hello.ts\";\n".as_bytes() - ); - assert_eq!(&(r.media_type), &msg::MediaType::TypeScript); - // Should not create .mime file due to matching ext - assert!(fs::read_to_string(&mime_file_name).is_err()); - - // 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".as_bytes() - ); - // 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, false, 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(); - let expected3 = - "export { printHello } from \"./print_hello.ts\";\n".as_bytes(); - assert_eq!(r3.source_code, expected3); - // Now the old .mime file should have gone! Resolved back to TypeScript - assert_eq!(&(r3.media_type), &msg::MediaType::TypeScript); - assert!(fs::read_to_string(&mime_file_name).is_err()); - }); - } - - #[test] - fn test_get_source_code_2() { - let (temp_dir, deno_dir) = test_setup(false, false); - // http_util::fetch_sync_string requires tokio - tokio_util::init(|| { - let module_name = "http://localhost:4545/tests/subdir/mismatch_ext.ts"; - let filename = deno_fs::normalize_path( - deno_dir - .deps_http - .join("localhost_PORT4545/tests/subdir/mismatch_ext.ts") - .as_ref(), - ); - let mime_file_name = format!("{}.mime", &filename); - - let result = deno_dir.get_source_code(module_name, &filename); - println!("module_name {} filename {}", module_name, filename); - assert!(result.is_ok()); - let r = result.unwrap(); - let expected = "export const loaded = true;\n".as_bytes(); - assert_eq!(r.source_code, expected); - // Mismatch ext with content type, create .mime - assert_eq!(&(r.media_type), &msg::MediaType::JavaScript); - assert_eq!( - fs::read_to_string(&mime_file_name).unwrap(), - "text/javascript" - ); - - // Modify .mime - let _ = fs::write(&mime_file_name, "text/typescript"); - let result2 = deno_dir.get_source_code(module_name, &filename); - assert!(result2.is_ok()); - let r2 = result2.unwrap(); - let expected2 = "export const loaded = true;\n".as_bytes(); - assert_eq!(r2.source_code, expected2); - // If get_source_code does not call remote, this should be TypeScript - // as we modified before! (we do not overwrite .mime due to no http fetch) - assert_eq!(&(r2.media_type), &msg::MediaType::TypeScript); - assert_eq!( - fs::read_to_string(&mime_file_name).unwrap(), - "text/typescript" - ); - - // Force self.reload - let deno_dir = - DenoDir::new(true, false, 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(); - let expected3 = "export const loaded = true;\n".as_bytes(); - assert_eq!(r3.source_code, expected3); - // Now the old .mime file should be overwritten back to JavaScript! - // (due to http fetch) - assert_eq!(&(r3.media_type), &msg::MediaType::JavaScript); - assert_eq!( - fs::read_to_string(&mime_file_name).unwrap(), - "text/javascript" - ); - }); - } - - #[test] - fn test_fetch_source_1() { - use crate::tokio_util; - // http_util::fetch_sync_string requires tokio - tokio_util::init(|| { - let (_temp_dir, deno_dir) = test_setup(false, false); - let module_name = - "http://localhost:4545/tests/subdir/mt_video_mp2t.t3.ts"; - let filename = deno_fs::normalize_path( - deno_dir - .deps_http - .join("localhost_PORT4545/tests/subdir/mt_video_mp2t.t3.ts") - .as_ref(), - ); - let mime_file_name = format!("{}.mime", &filename); - - let result = deno_dir.fetch_remote_source(module_name, &filename); - assert!(result.is_ok()); - let r = result.unwrap().unwrap(); - assert_eq!(r.source_code, "export const loaded = true;\n".as_bytes()); - assert_eq!(&(r.media_type), &msg::MediaType::TypeScript); - // matching ext, no .mime file created - assert!(fs::read_to_string(&mime_file_name).is_err()); - - // Modify .mime, make sure read from local - let _ = fs::write(&mime_file_name, "text/javascript"); - let result2 = deno_dir.fetch_local_source(module_name, &filename); - assert!(result2.is_ok()); - let r2 = result2.unwrap().unwrap(); - assert_eq!(r2.source_code, "export const loaded = true;\n".as_bytes()); - // Not MediaType::TypeScript due to .mime modification - assert_eq!(&(r2.media_type), &msg::MediaType::JavaScript); - }); - } - - #[test] - fn test_fetch_source_2() { - use crate::tokio_util; - // http_util::fetch_sync_string requires tokio - tokio_util::init(|| { - let (_temp_dir, deno_dir) = test_setup(false, false); - let module_name = "http://localhost:4545/tests/subdir/no_ext"; - let filename = deno_fs::normalize_path( - deno_dir - .deps_http - .join("localhost_PORT4545/tests/subdir/no_ext") - .as_ref(), - ); - let mime_file_name = format!("{}.mime", &filename); - let result = deno_dir.fetch_remote_source(module_name, &filename); - assert!(result.is_ok()); - let r = result.unwrap().unwrap(); - assert_eq!(r.source_code, "export const loaded = true;\n".as_bytes()); - assert_eq!(&(r.media_type), &msg::MediaType::TypeScript); - // no ext, should create .mime file - assert_eq!( - fs::read_to_string(&mime_file_name).unwrap(), - "text/typescript" - ); - - let module_name_2 = "http://localhost:4545/tests/subdir/mismatch_ext.ts"; - let filename_2 = deno_fs::normalize_path( - deno_dir - .deps_http - .join("localhost_PORT4545/tests/subdir/mismatch_ext.ts") - .as_ref(), - ); - let mime_file_name_2 = format!("{}.mime", &filename_2); - let result_2 = deno_dir.fetch_remote_source(module_name_2, &filename_2); - assert!(result_2.is_ok()); - let r2 = result_2.unwrap().unwrap(); - assert_eq!(r2.source_code, "export const loaded = true;\n".as_bytes()); - assert_eq!(&(r2.media_type), &msg::MediaType::JavaScript); - // mismatch ext, should create .mime file - assert_eq!( - fs::read_to_string(&mime_file_name_2).unwrap(), - "text/javascript" - ); - - // test unknown extension - let module_name_3 = "http://localhost:4545/tests/subdir/unknown_ext.deno"; - let filename_3 = deno_fs::normalize_path( - deno_dir - .deps_http - .join("localhost_PORT4545/tests/subdir/unknown_ext.deno") - .as_ref(), - ); - let mime_file_name_3 = format!("{}.mime", &filename_3); - let result_3 = deno_dir.fetch_remote_source(module_name_3, &filename_3); - assert!(result_3.is_ok()); - let r3 = result_3.unwrap().unwrap(); - assert_eq!(r3.source_code, "export const loaded = true;\n".as_bytes()); - assert_eq!(&(r3.media_type), &msg::MediaType::TypeScript); - // unknown ext, should create .mime file - assert_eq!( - fs::read_to_string(&mime_file_name_3).unwrap(), - "text/typescript" - ); - }); - } - - #[test] - fn test_fetch_source_3() { - // only local, no http_util::fetch_sync_string called - let (_temp_dir, deno_dir) = test_setup(false, false); - let cwd = std::env::current_dir().unwrap(); - let cwd_string = cwd.to_str().unwrap(); - let module_name = "http://example.com/mt_text_typescript.t1.ts"; // not used - let filename = - format!("{}/tests/subdir/mt_text_typescript.t1.ts", &cwd_string); - - let result = deno_dir.fetch_local_source(module_name, &filename); - assert!(result.is_ok()); - let r = result.unwrap().unwrap(); - assert_eq!(r.source_code, "export const loaded = true;\n".as_bytes()); - assert_eq!(&(r.media_type), &msg::MediaType::TypeScript); - } - - #[test] - fn test_fetch_module_meta_data() { - let (_temp_dir, deno_dir) = test_setup(false, false); - - let cwd = std::env::current_dir().unwrap(); - let cwd_string = String::from(cwd.to_str().unwrap()) + "/"; - - // Test failure case. - let specifier = "hello.ts"; - let referrer = add_root!("/baddir/badfile.ts"); - let r = deno_dir.fetch_module_meta_data(specifier, referrer); - 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); - assert!(r.is_ok()); - //let fetch_module_meta_data_output = r.unwrap(); - //println!("fetch_module_meta_data_output {:?}", fetch_module_meta_data_output); - } - - #[test] - fn test_fetch_module_meta_data_1() { - /*recompile ts file*/ - let (_temp_dir, deno_dir) = test_setup(false, true); - - let cwd = std::env::current_dir().unwrap(); - let cwd_string = String::from(cwd.to_str().unwrap()) + "/"; - - // Test failure case. - let specifier = "hello.ts"; - let referrer = add_root!("/baddir/badfile.ts"); - let r = deno_dir.fetch_module_meta_data(specifier, referrer); - 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); - assert!(r.is_ok()); - } - - #[test] - fn test_src_file_to_url_1() { - let (_temp_dir, deno_dir) = test_setup(false, false); - assert_eq!("hello", deno_dir.src_file_to_url("hello")); - assert_eq!("/hello", deno_dir.src_file_to_url("/hello")); - let x = deno_dir.deps_http.join("hello/world.txt"); - assert_eq!( - "http://hello/world.txt", - deno_dir.src_file_to_url(x.to_str().unwrap()) - ); - } - - #[test] - fn test_src_file_to_url_2() { - let (_temp_dir, deno_dir) = test_setup(false, false); - assert_eq!("hello", deno_dir.src_file_to_url("hello")); - assert_eq!("/hello", deno_dir.src_file_to_url("/hello")); - let x = deno_dir.deps_https.join("hello/world.txt"); - assert_eq!( - "https://hello/world.txt", - deno_dir.src_file_to_url(x.to_str().unwrap()) - ); - } - - #[test] - fn test_src_file_to_url_3() { - let (_temp_dir, deno_dir) = test_setup(false, false); - let x = deno_dir.deps_http.join("localhost_PORT4545/world.txt"); - assert_eq!( - "http://localhost:4545/world.txt", - deno_dir.src_file_to_url(x.to_str().unwrap()) - ); - } - - #[test] - fn test_src_file_to_url_4() { - let (_temp_dir, deno_dir) = test_setup(false, false); - let x = deno_dir.deps_https.join("localhost_PORT4545/world.txt"); - assert_eq!( - "https://localhost:4545/world.txt", - deno_dir.src_file_to_url(x.to_str().unwrap()) - ); - } - - // https://github.com/denoland/deno/blob/golang/os_test.go#L16-L87 - #[test] - fn test_resolve_module_1() { - let (_temp_dir, deno_dir) = test_setup(false, false); - - let test_cases = [ - ( - "./subdir/print_hello.ts", - add_root!("/Users/rld/go/src/github.com/denoland/deno/testdata/006_url_imports.ts"), - file_url!("/Users/rld/go/src/github.com/denoland/deno/testdata/subdir/print_hello.ts"), - add_root!("/Users/rld/go/src/github.com/denoland/deno/testdata/subdir/print_hello.ts"), - ), - ( - "testdata/001_hello.js", - add_root!("/Users/rld/go/src/github.com/denoland/deno/"), - file_url!("/Users/rld/go/src/github.com/denoland/deno/testdata/001_hello.js"), - add_root!("/Users/rld/go/src/github.com/denoland/deno/testdata/001_hello.js"), - ), - ( - add_root!("/Users/rld/src/deno/hello.js"), - ".", - file_url!("/Users/rld/src/deno/hello.js"), - add_root!("/Users/rld/src/deno/hello.js"), - ), - ( - add_root!("/this/module/got/imported.js"), - add_root!("/that/module/did/it.js"), - file_url!("/this/module/got/imported.js"), - add_root!("/this/module/got/imported.js"), - ), - ]; - for &test in test_cases.iter() { - let specifier = String::from(test.0); - let referrer = String::from(test.1); - let (module_name, filename) = - deno_dir.resolve_module(&specifier, &referrer).unwrap(); - assert_eq!(module_name, test.2); - assert_eq!(filename, test.3); - } - } - - #[test] - fn test_resolve_module_2() { - let (_temp_dir, deno_dir) = test_setup(false, false); - - let specifier = "http://localhost:4545/testdata/subdir/print_hello.ts"; - let referrer = add_root!("/deno/testdata/006_url_imports.ts"); - - let expected_module_name = - "http://localhost:4545/testdata/subdir/print_hello.ts"; - let expected_filename = deno_fs::normalize_path( - deno_dir - .deps_http - .join("localhost_PORT4545/testdata/subdir/print_hello.ts") - .as_ref(), - ); - - let (module_name, filename) = - deno_dir.resolve_module(specifier, referrer).unwrap(); - assert_eq!(module_name, expected_module_name); - assert_eq!(filename, expected_filename); - } - - #[test] - fn test_resolve_module_3() { - let (_temp_dir, deno_dir) = test_setup(false, false); - - let specifier_ = - deno_dir.deps_http.join("unpkg.com/liltest@0.0.5/index.ts"); - let specifier = specifier_.to_str().unwrap(); - let referrer = "."; - - let expected_module_name = "http://unpkg.com/liltest@0.0.5/index.ts"; - let expected_filename = deno_fs::normalize_path( - deno_dir - .deps_http - .join("unpkg.com/liltest@0.0.5/index.ts") - .as_ref(), - ); - - let (module_name, filename) = - deno_dir.resolve_module(specifier, referrer).unwrap(); - assert_eq!(module_name, expected_module_name); - assert_eq!(filename, expected_filename); - } - - #[test] - fn test_resolve_module_4() { - let (_temp_dir, deno_dir) = test_setup(false, false); - - let specifier = "./util"; - let referrer_ = deno_dir.deps_http.join("unpkg.com/liltest@0.0.5/index.ts"); - let referrer = referrer_.to_str().unwrap(); - - // http containing files -> load relative import with http - let expected_module_name = "http://unpkg.com/liltest@0.0.5/util"; - let expected_filename = deno_fs::normalize_path( - deno_dir - .deps_http - .join("unpkg.com/liltest@0.0.5/util") - .as_ref(), - ); - - let (module_name, filename) = - deno_dir.resolve_module(specifier, referrer).unwrap(); - assert_eq!(module_name, expected_module_name); - assert_eq!(filename, expected_filename); - } - - #[test] - fn test_resolve_module_5() { - let (_temp_dir, deno_dir) = test_setup(false, false); - - let specifier = "./util"; - let referrer_ = - deno_dir.deps_https.join("unpkg.com/liltest@0.0.5/index.ts"); - let referrer = referrer_.to_str().unwrap(); - - // https containing files -> load relative import with https - let expected_module_name = "https://unpkg.com/liltest@0.0.5/util"; - let expected_filename = deno_fs::normalize_path( - deno_dir - .deps_https - .join("unpkg.com/liltest@0.0.5/util") - .as_ref(), - ); - - let (module_name, filename) = - deno_dir.resolve_module(specifier, referrer).unwrap(); - assert_eq!(module_name, expected_module_name); - assert_eq!(filename, expected_filename); - } - - #[test] - fn test_resolve_module_6() { - let (_temp_dir, deno_dir) = test_setup(false, false); - - let specifier = "http://localhost:4545/tests/subdir/mod2.ts"; - let referrer = add_root!("/deno/tests/006_url_imports.ts"); - let expected_module_name = "http://localhost:4545/tests/subdir/mod2.ts"; - let expected_filename = deno_fs::normalize_path( - deno_dir - .deps_http - .join("localhost_PORT4545/tests/subdir/mod2.ts") - .as_ref(), - ); - - let (module_name, filename) = - deno_dir.resolve_module(specifier, referrer).unwrap(); - assert_eq!(module_name, expected_module_name); - assert_eq!(filename, expected_filename); - } - - #[test] - fn test_resolve_module_7() { - let (_temp_dir, deno_dir) = test_setup(false, false); - - let specifier = "http_test.ts"; - let referrer = add_root!("/Users/rld/src/deno_net/"); - let expected_module_name = - file_url!("/Users/rld/src/deno_net/http_test.ts"); - let expected_filename = add_root!("/Users/rld/src/deno_net/http_test.ts"); - - let (module_name, filename) = - deno_dir.resolve_module(specifier, referrer).unwrap(); - assert_eq!(module_name, expected_module_name); - assert_eq!(filename, expected_filename); - } - - #[test] - fn test_resolve_module_referrer_dot() { - let (_temp_dir, deno_dir) = test_setup(false, false); - - let specifier = "tests/001_hello.js"; - - let cwd = std::env::current_dir().unwrap(); - let expected_path = cwd.join(specifier); - let expected_module_name = - Url::from_file_path(&expected_path).unwrap().to_string(); - let expected_filename = deno_fs::normalize_path(&expected_path); - - let (module_name, filename) = - deno_dir.resolve_module(specifier, ".").unwrap(); - assert_eq!(module_name, expected_module_name); - assert_eq!(filename, expected_filename); - - let (module_name, filename) = - deno_dir.resolve_module(specifier, "./").unwrap(); - assert_eq!(module_name, expected_module_name); - assert_eq!(filename, expected_filename); - } - - #[test] - fn test_resolve_module_referrer_dotdot() { - let (_temp_dir, deno_dir) = test_setup(false, false); - - let specifier = "tests/001_hello.js"; - - let cwd = std::env::current_dir().unwrap(); - let expected_path = cwd.join("..").join(specifier); - let expected_module_name = - Url::from_file_path(&expected_path).unwrap().to_string(); - let expected_filename = deno_fs::normalize_path(&expected_path); - - let (module_name, filename) = - deno_dir.resolve_module(specifier, "..").unwrap(); - assert_eq!(module_name, expected_module_name); - assert_eq!(filename, expected_filename); - - let (module_name, filename) = - deno_dir.resolve_module(specifier, "../").unwrap(); - assert_eq!(module_name, expected_module_name); - assert_eq!(filename, expected_filename); - } - - #[test] - fn test_map_file_extension() { - assert_eq!( - map_file_extension(Path::new("foo/bar.ts")), - msg::MediaType::TypeScript - ); - assert_eq!( - map_file_extension(Path::new("foo/bar.d.ts")), - msg::MediaType::TypeScript - ); - assert_eq!( - map_file_extension(Path::new("foo/bar.js")), - msg::MediaType::JavaScript - ); - assert_eq!( - map_file_extension(Path::new("foo/bar.json")), - msg::MediaType::Json - ); - assert_eq!( - map_file_extension(Path::new("foo/bar.txt")), - msg::MediaType::Unknown - ); - assert_eq!( - map_file_extension(Path::new("foo/bar")), - msg::MediaType::Unknown - ); - } - - #[test] - fn test_map_content_type() { - // Extension only - assert_eq!( - map_content_type(Path::new("foo/bar.ts"), None), - msg::MediaType::TypeScript - ); - assert_eq!( - map_content_type(Path::new("foo/bar.d.ts"), None), - msg::MediaType::TypeScript - ); - assert_eq!( - map_content_type(Path::new("foo/bar.js"), None), - msg::MediaType::JavaScript - ); - assert_eq!( - map_content_type(Path::new("foo/bar.json"), None), - msg::MediaType::Json - ); - assert_eq!( - map_content_type(Path::new("foo/bar.txt"), None), - msg::MediaType::Unknown - ); - assert_eq!( - map_content_type(Path::new("foo/bar"), None), - msg::MediaType::Unknown - ); - - // Media Type - assert_eq!( - map_content_type(Path::new("foo/bar"), Some("application/typescript")), - msg::MediaType::TypeScript - ); - assert_eq!( - map_content_type(Path::new("foo/bar"), Some("text/typescript")), - msg::MediaType::TypeScript - ); - assert_eq!( - map_content_type(Path::new("foo/bar"), Some("video/vnd.dlna.mpeg-tts")), - msg::MediaType::TypeScript - ); - assert_eq!( - map_content_type(Path::new("foo/bar"), Some("video/mp2t")), - msg::MediaType::TypeScript - ); - assert_eq!( - map_content_type(Path::new("foo/bar"), Some("application/x-typescript")), - msg::MediaType::TypeScript - ); - assert_eq!( - map_content_type(Path::new("foo/bar"), Some("application/javascript")), - msg::MediaType::JavaScript - ); - assert_eq!( - map_content_type(Path::new("foo/bar"), Some("text/javascript")), - msg::MediaType::JavaScript - ); - assert_eq!( - map_content_type(Path::new("foo/bar"), Some("application/ecmascript")), - msg::MediaType::JavaScript - ); - assert_eq!( - map_content_type(Path::new("foo/bar"), Some("text/ecmascript")), - msg::MediaType::JavaScript - ); - assert_eq!( - map_content_type(Path::new("foo/bar"), Some("application/x-javascript")), - msg::MediaType::JavaScript - ); - assert_eq!( - map_content_type(Path::new("foo/bar"), Some("application/json")), - msg::MediaType::Json - ); - assert_eq!( - map_content_type(Path::new("foo/bar"), Some("text/json")), - msg::MediaType::Json - ); - assert_eq!( - map_content_type(Path::new("foo/bar.ts"), Some("text/plain")), - msg::MediaType::TypeScript - ); - assert_eq!( - map_content_type(Path::new("foo/bar.ts"), Some("foo/bar")), - msg::MediaType::Unknown - ); - } - - #[test] - fn test_filter_shebang() { - assert_eq!(filter_shebang("#!".as_bytes().to_owned()), "".as_bytes()); - assert_eq!( - filter_shebang("#!\n\n".as_bytes().to_owned()), - "\n\n".as_bytes() - ); - let code = "#!/usr/bin/env deno\nconsole.log('hello');\n" - .as_bytes() - .to_owned(); - assert_eq!(filter_shebang(code), "\nconsole.log('hello');\n".as_bytes()); - } -} |