diff options
Diffstat (limited to 'cli/media_type.rs')
-rw-r--r-- | cli/media_type.rs | 159 |
1 files changed, 149 insertions, 10 deletions
diff --git a/cli/media_type.rs b/cli/media_type.rs index c83716f67..bfb869c13 100644 --- a/cli/media_type.rs +++ b/cli/media_type.rs @@ -1,5 +1,6 @@ // Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. +use data_url::DataUrl; use deno_core::serde::Serialize; use deno_core::serde::Serializer; use deno_core::ModuleSpecifier; @@ -45,34 +46,40 @@ impl fmt::Display for MediaType { impl<'a> From<&'a Path> for MediaType { fn from(path: &'a Path) -> Self { - MediaType::from_path(path) + Self::from_path(path) } } impl<'a> From<&'a PathBuf> for MediaType { fn from(path: &'a PathBuf) -> Self { - MediaType::from_path(path) + Self::from_path(path) } } impl<'a> From<&'a String> for MediaType { fn from(specifier: &'a String) -> Self { - MediaType::from_path(&PathBuf::from(specifier)) + Self::from_path(&PathBuf::from(specifier)) } } impl<'a> From<&'a ModuleSpecifier> for MediaType { fn from(specifier: &'a ModuleSpecifier) -> Self { - let path = if specifier.scheme() == "file" { - if let Ok(path) = specifier.to_file_path() { - path + if specifier.scheme() != "data" { + let path = if specifier.scheme() == "file" { + if let Ok(path) = specifier.to_file_path() { + path + } else { + PathBuf::from(specifier.path()) + } } else { PathBuf::from(specifier.path()) - } + }; + Self::from_path(&path) + } else if let Ok(data_url) = DataUrl::process(specifier.as_str()) { + Self::from_content_type(specifier, data_url.mime_type().to_string()) } else { - PathBuf::from(specifier.path()) - }; - MediaType::from_path(&path) + Self::Unknown + } } } @@ -83,6 +90,40 @@ impl Default for MediaType { } impl MediaType { + pub fn from_content_type<S: AsRef<str>>( + specifier: &ModuleSpecifier, + content_type: S, + ) -> Self { + match content_type.as_ref().trim().to_lowercase().as_ref() { + "application/typescript" + | "text/typescript" + | "video/vnd.dlna.mpeg-tts" + | "video/mp2t" + | "application/x-typescript" => { + map_js_like_extension(specifier, Self::TypeScript) + } + "application/javascript" + | "text/javascript" + | "application/ecmascript" + | "text/ecmascript" + | "application/x-javascript" + | "application/node" => { + map_js_like_extension(specifier, Self::JavaScript) + } + "text/jsx" => Self::Jsx, + "text/tsx" => Self::Tsx, + "application/json" | "text/json" => Self::Json, + "application/wasm" => Self::Wasm, + // Handle plain and possibly webassembly + "text/plain" | "application/octet-stream" + if specifier.scheme() != "data" => + { + Self::from(specifier) + } + _ => Self::Unknown, + } + } + fn from_path(path: &Path) -> Self { match path.extension() { None => match path.file_name() { @@ -197,6 +238,55 @@ where } } +/// Used to augment media types by using the path part of a module specifier to +/// resolve to a more accurate media type. +fn map_js_like_extension( + specifier: &ModuleSpecifier, + default: MediaType, +) -> MediaType { + let path = if specifier.scheme() == "file" { + if let Ok(path) = specifier.to_file_path() { + path + } else { + PathBuf::from(specifier.path()) + } + } else { + PathBuf::from(specifier.path()) + }; + match path.extension() { + None => default, + Some(os_str) => match os_str.to_str() { + None => default, + Some("jsx") => MediaType::Jsx, + Some("tsx") => MediaType::Tsx, + // Because DTS files do not have a separate media type, or a unique + // extension, we have to "guess" at those things that we consider that + // look like TypeScript, and end with `.d.ts` are DTS files. + Some("ts") => { + if default == MediaType::TypeScript { + match path.file_stem() { + None => default, + Some(os_str) => { + if let Some(file_stem) = os_str.to_str() { + if file_stem.ends_with(".d") { + MediaType::Dts + } else { + default + } + } else { + default + } + } + } + } else { + default + } + } + Some(_) => default, + }, + } +} + #[cfg(test)] mod tests { use super::*; @@ -245,6 +335,9 @@ mod tests { ("https://deno.land/x/mod.ts", MediaType::TypeScript), ("https://deno.land/x/mod.js", MediaType::JavaScript), ("https://deno.land/x/mod.txt", MediaType::Unknown), + ("data:application/typescript;base64,ZXhwb3J0IGNvbnN0IGEgPSAiYSI7CgpleHBvcnQgZW51bSBBIHsKICBBLAogIEIsCiAgQywKfQo=", MediaType::TypeScript), + ("data:application/javascript;base64,ZXhwb3J0IGNvbnN0IGEgPSAiYSI7CgpleHBvcnQgZW51bSBBIHsKICBBLAogIEIsCiAgQywKfQo=", MediaType::JavaScript), + ("data:text/plain;base64,ZXhwb3J0IGNvbnN0IGEgPSAiYSI7CgpleHBvcnQgZW51bSBBIHsKICBBLAogIEIsCiAgQywKfQo=", MediaType::Unknown), ]; for (specifier, expected) in fixtures { @@ -254,6 +347,52 @@ mod tests { } #[test] + fn test_from_content_type() { + let fixtures = vec![ + ( + "https://deno.land/x/mod.ts", + "application/typescript", + MediaType::TypeScript, + ), + ( + "https://deno.land/x/mod.d.ts", + "application/typescript", + MediaType::Dts, + ), + ("https://deno.land/x/mod.tsx", "text/tsx", MediaType::Tsx), + ( + "https://deno.land/x/mod.js", + "application/javascript", + MediaType::JavaScript, + ), + ("https://deno.land/x/mod.jsx", "text/jsx", MediaType::Jsx), + ( + "https://deno.land/x/mod.ts", + "text/plain", + MediaType::TypeScript, + ), + ( + "https://deno.land/x/mod.js", + "text/plain", + MediaType::JavaScript, + ), + ( + "https://deno.land/x/mod.wasm", + "text/plain", + MediaType::Wasm, + ), + ]; + + for (specifier, content_type, expected) in fixtures { + let fixture = deno_core::resolve_url_or_path(specifier).unwrap(); + assert_eq!( + MediaType::from_content_type(&fixture, content_type), + expected + ); + } + } + + #[test] fn test_serialization() { assert_eq!(json!(MediaType::JavaScript), json!(0)); assert_eq!(json!(MediaType::Jsx), json!(1)); |