From c7dabc99eed50fa20cdcafd7c0175ab615da3d50 Mon Sep 17 00:00:00 2001 From: Ryan Dahl Date: Wed, 17 Feb 2021 13:47:18 -0500 Subject: [PATCH] Make ModuleSpecifier a type alias, not wrapper struct (#9531) --- cli/ast.rs | 24 +-- cli/auth_tokens.rs | 38 ++-- cli/file_fetcher.rs | 220 ++++++++++------------ cli/import_map.rs | 128 ++++++------- cli/info.rs | 14 +- cli/lsp/analysis.rs | 21 +-- cli/lsp/diagnostics.rs | 4 +- cli/lsp/documents.rs | 10 +- cli/lsp/language_server.rs | 22 +-- cli/lsp/sources.rs | 74 +++----- cli/lsp/tsc.rs | 54 +++--- cli/lsp/utils.rs | 20 +- cli/main.rs | 55 +++--- cli/media_type.rs | 11 +- cli/module_graph.rs | 95 ++++------ cli/module_loader.rs | 5 +- cli/ops/runtime_compiler.rs | 12 +- cli/program_state.rs | 9 +- cli/source_maps.rs | 4 +- cli/specifier_handler.rs | 62 +++---- cli/standalone.rs | 5 +- cli/tools/coverage.rs | 3 +- cli/tsc.rs | 70 +++---- core/bindings.rs | 8 +- core/lib.rs | 5 + core/module_specifier.rs | 292 ++++++++++++------------------ core/modules.rs | 54 +++--- core/runtime.rs | 15 +- runtime/examples/hello_runtime.rs | 3 +- runtime/ops/runtime.rs | 4 +- runtime/ops/worker_host.rs | 2 +- runtime/permissions.rs | 43 ++--- runtime/web_worker.rs | 3 +- runtime/worker.rs | 16 +- 34 files changed, 599 insertions(+), 806 deletions(-) diff --git a/cli/ast.rs b/cli/ast.rs index 7aeb1cebc2..156faf9721 100644 --- a/cli/ast.rs +++ b/cli/ast.rs @@ -4,6 +4,7 @@ use crate::media_type::MediaType; use crate::tsc_config; use deno_core::error::AnyError; +use deno_core::resolve_url_or_path; use deno_core::serde_json; use deno_core::ModuleSpecifier; use std::error::Error; @@ -78,7 +79,7 @@ impl Into for swc_common::Loc { impl Into for Location { fn into(self) -> ModuleSpecifier { - ModuleSpecifier::resolve_url_or_path(&self.filename).unwrap() + resolve_url_or_path(&self.filename).unwrap() } } @@ -593,9 +594,7 @@ mod tests { #[test] fn test_parsed_module_analyze_dependencies() { - let specifier = - ModuleSpecifier::resolve_url_or_path("https://deno.land/x/mod.js") - .unwrap(); + let specifier = resolve_url_or_path("https://deno.land/x/mod.js").unwrap(); let source = r#"import * as bar from "./test.ts"; const foo = await import("./foo.ts"); "#; @@ -634,9 +633,8 @@ mod tests { #[test] fn test_transpile() { - let specifier = - ModuleSpecifier::resolve_url_or_path("https://deno.land/x/mod.ts") - .expect("could not resolve specifier"); + let specifier = resolve_url_or_path("https://deno.land/x/mod.ts") + .expect("could not resolve specifier"); let source = r#" enum D { A, @@ -668,9 +666,8 @@ mod tests { #[test] fn test_transpile_tsx() { - let specifier = - ModuleSpecifier::resolve_url_or_path("https://deno.land/x/mod.ts") - .expect("could not resolve specifier"); + let specifier = resolve_url_or_path("https://deno.land/x/mod.ts") + .expect("could not resolve specifier"); let source = r#" export class A { render() { @@ -688,9 +685,8 @@ mod tests { #[test] fn test_transpile_decorators() { - let specifier = - ModuleSpecifier::resolve_url_or_path("https://deno.land/x/mod.ts") - .expect("could not resolve specifier"); + let specifier = resolve_url_or_path("https://deno.land/x/mod.ts") + .expect("could not resolve specifier"); let source = r#" function enumerable(value: boolean) { return function ( @@ -701,7 +697,7 @@ mod tests { descriptor.enumerable = value; }; } - + export class A { @enumerable(false) a() { diff --git a/cli/auth_tokens.rs b/cli/auth_tokens.rs index f52f564e15..64f42fc5c0 100644 --- a/cli/auth_tokens.rs +++ b/cli/auth_tokens.rs @@ -52,11 +52,10 @@ impl AuthTokens { /// matching is case insensitive. pub fn get(&self, specifier: &ModuleSpecifier) -> Option { self.0.iter().find_map(|t| { - let url = specifier.as_url(); - let hostname = if let Some(port) = url.port() { - format!("{}:{}", url.host_str()?, port) + let hostname = if let Some(port) = specifier.port() { + format!("{}:{}", specifier.host_str()?, port) } else { - url.host_str()?.to_string() + specifier.host_str()?.to_string() }; if hostname.to_lowercase().ends_with(&t.host) { Some(t.clone()) @@ -70,31 +69,27 @@ impl AuthTokens { #[cfg(test)] mod tests { use super::*; + use deno_core::resolve_url; #[test] fn test_auth_token() { let auth_tokens = AuthTokens::new(Some("abc123@deno.land".to_string())); - let fixture = - ModuleSpecifier::resolve_url("https://deno.land/x/mod.ts").unwrap(); + let fixture = resolve_url("https://deno.land/x/mod.ts").unwrap(); assert_eq!( auth_tokens.get(&fixture).unwrap().to_string(), "Bearer abc123" ); - let fixture = - ModuleSpecifier::resolve_url("https://www.deno.land/x/mod.ts").unwrap(); + let fixture = resolve_url("https://www.deno.land/x/mod.ts").unwrap(); assert_eq!( auth_tokens.get(&fixture).unwrap().to_string(), "Bearer abc123".to_string() ); - let fixture = - ModuleSpecifier::resolve_url("http://127.0.0.1:8080/x/mod.ts").unwrap(); + let fixture = resolve_url("http://127.0.0.1:8080/x/mod.ts").unwrap(); assert_eq!(auth_tokens.get(&fixture), None); let fixture = - ModuleSpecifier::resolve_url("https://deno.land.example.com/x/mod.ts") - .unwrap(); + resolve_url("https://deno.land.example.com/x/mod.ts").unwrap(); assert_eq!(auth_tokens.get(&fixture), None); - let fixture = - ModuleSpecifier::resolve_url("https://deno.land:8080/x/mod.ts").unwrap(); + let fixture = resolve_url("https://deno.land:8080/x/mod.ts").unwrap(); assert_eq!(auth_tokens.get(&fixture), None); } @@ -102,14 +97,12 @@ mod tests { fn test_auth_tokens_multiple() { let auth_tokens = AuthTokens::new(Some("abc123@deno.land;def456@example.com".to_string())); - let fixture = - ModuleSpecifier::resolve_url("https://deno.land/x/mod.ts").unwrap(); + let fixture = resolve_url("https://deno.land/x/mod.ts").unwrap(); assert_eq!( auth_tokens.get(&fixture).unwrap().to_string(), "Bearer abc123".to_string() ); - let fixture = - ModuleSpecifier::resolve_url("http://example.com/a/file.ts").unwrap(); + let fixture = resolve_url("http://example.com/a/file.ts").unwrap(); assert_eq!( auth_tokens.get(&fixture).unwrap().to_string(), "Bearer def456".to_string() @@ -120,11 +113,9 @@ mod tests { fn test_auth_tokens_port() { let auth_tokens = AuthTokens::new(Some("abc123@deno.land:8080".to_string())); - let fixture = - ModuleSpecifier::resolve_url("https://deno.land/x/mod.ts").unwrap(); + let fixture = resolve_url("https://deno.land/x/mod.ts").unwrap(); assert_eq!(auth_tokens.get(&fixture), None); - let fixture = - ModuleSpecifier::resolve_url("http://deno.land:8080/x/mod.ts").unwrap(); + let fixture = resolve_url("http://deno.land:8080/x/mod.ts").unwrap(); assert_eq!( auth_tokens.get(&fixture).unwrap().to_string(), "Bearer abc123".to_string() @@ -134,8 +125,7 @@ mod tests { #[test] fn test_auth_tokens_contain_at() { let auth_tokens = AuthTokens::new(Some("abc@123@deno.land".to_string())); - let fixture = - ModuleSpecifier::resolve_url("https://deno.land/x/mod.ts").unwrap(); + let fixture = resolve_url("https://deno.land/x/mod.ts").unwrap(); assert_eq!( auth_tokens.get(&fixture).unwrap().to_string(), "Bearer abc@123".to_string() diff --git a/cli/file_fetcher.rs b/cli/file_fetcher.rs index 722813457d..4223654dc0 100644 --- a/cli/file_fetcher.rs +++ b/cli/file_fetcher.rs @@ -94,7 +94,7 @@ impl CacheSetting { CacheSetting::ReloadAll => false, CacheSetting::Use | CacheSetting::Only => true, CacheSetting::ReloadSome(list) => { - let mut url = specifier.as_url().clone(); + let mut url = specifier.clone(); url.set_fragment(None); if list.contains(&url.as_str().to_string()) { return false; @@ -117,7 +117,7 @@ impl CacheSetting { /// Fetch a source file from the local file system. fn fetch_local(specifier: &ModuleSpecifier) -> Result { - let local = specifier.as_url().to_file_path().map_err(|_| { + let local = specifier.to_file_path().map_err(|_| { uri_error(format!("Invalid file path.\n Specifier: {}", specifier)) })?; let bytes = fs::read(local.clone())?; @@ -152,14 +152,13 @@ pub fn get_source_from_bytes( fn get_source_from_data_url( specifier: &ModuleSpecifier, ) -> Result<(String, MediaType, String), AnyError> { - let url = specifier.as_url(); - if url.scheme() != "data" { + if specifier.scheme() != "data" { return Err(custom_error( "BadScheme", - format!("Unexpected scheme of \"{}\"", url.scheme()), + format!("Unexpected scheme of \"{}\"", specifier.scheme()), )); } - let path = url.path(); + let path = specifier.path(); let mut parts = path.splitn(2, ','); let media_type_part = percent_encoding::percent_decode_str(parts.next().unwrap()) @@ -188,7 +187,7 @@ fn get_source_from_data_url( fn get_validated_scheme( specifier: &ModuleSpecifier, ) -> Result { - let scheme = specifier.as_url().scheme(); + let scheme = specifier.scheme(); if !SUPPORTED_SCHEMES.contains(&scheme) { Err(generic_error(format!( "Unsupported scheme \"{}\" for module \"{}\". Supported schemes: {:#?}", @@ -252,15 +251,14 @@ fn map_js_like_extension( specifier: &ModuleSpecifier, default: MediaType, ) -> MediaType { - let url = specifier.as_url(); - let path = if url.scheme() == "file" { - if let Ok(path) = url.to_file_path() { + let path = if specifier.scheme() == "file" { + if let Ok(path) = specifier.to_file_path() { path } else { - PathBuf::from(url.path()) + PathBuf::from(specifier.path()) } } else { - PathBuf::from(url.path()) + PathBuf::from(specifier.path()) }; match path.extension() { None => default, @@ -344,12 +342,13 @@ impl FileFetcher { bytes: Vec, headers: &HashMap, ) -> Result { - let local = self - .http_cache - .get_cache_filename(specifier.as_url()) - .ok_or_else(|| { - generic_error("Cannot convert specifier to cached filename.") - })?; + let local = + self + .http_cache + .get_cache_filename(specifier) + .ok_or_else(|| { + generic_error("Cannot convert specifier to cached filename.") + })?; let maybe_content_type = headers.get("content-type").cloned(); let (media_type, maybe_charset) = map_content_type(specifier, maybe_content_type); @@ -378,21 +377,20 @@ impl FileFetcher { return Err(custom_error("Http", "Too many redirects.")); } - let (mut source_file, headers) = - match self.http_cache.get(specifier.as_url()) { - Err(err) => { - if let Some(err) = err.downcast_ref::() { - if err.kind() == std::io::ErrorKind::NotFound { - return Ok(None); - } + let (mut source_file, headers) = match self.http_cache.get(specifier) { + Err(err) => { + if let Some(err) = err.downcast_ref::() { + if err.kind() == std::io::ErrorKind::NotFound { + return Ok(None); } - return Err(err); } - Ok(cache) => cache, - }; + return Err(err); + } + Ok(cache) => cache, + }; if let Some(redirect_to) = headers.get("location") { let redirect = - ModuleSpecifier::resolve_import(redirect_to, specifier.as_str())?; + deno_core::resolve_import(redirect_to, specifier.as_str())?; return self.fetch_cached(&redirect, redirect_limit - 1); } let mut bytes = Vec::new(); @@ -427,17 +425,16 @@ impl FileFetcher { let (source, media_type, content_type) = get_source_from_data_url(specifier)?; - let local = self - .http_cache - .get_cache_filename(specifier.as_url()) - .ok_or_else(|| { - generic_error("Cannot convert specifier to cached filename.") - })?; + let local = + self + .http_cache + .get_cache_filename(specifier) + .ok_or_else(|| { + generic_error("Cannot convert specifier to cached filename.") + })?; let mut headers = HashMap::new(); headers.insert("content-type".to_string(), content_type); - self - .http_cache - .set(specifier.as_url(), headers, source.as_bytes())?; + self.http_cache.set(specifier, headers, source.as_bytes())?; Ok(File { local, @@ -494,7 +491,7 @@ impl FileFetcher { info!("{} {}", colors::green("Download"), specifier); - let maybe_etag = match self.http_cache.get(specifier.as_url()) { + let maybe_etag = match self.http_cache.get(specifier) { Ok((_, headers)) => headers.get("etag").cloned(), _ => None, }; @@ -507,7 +504,7 @@ impl FileFetcher { async move { match fetch_once(FetchOnceArgs { client, - url: specifier.as_url().clone(), + url: specifier.clone(), maybe_etag, maybe_auth_token, }) @@ -518,20 +515,15 @@ impl FileFetcher { Ok(file) } FetchOnceResult::Redirect(redirect_url, headers) => { + file_fetcher.http_cache.set(&specifier, headers, &[])?; file_fetcher - .http_cache - .set(specifier.as_url(), headers, &[])?; - let redirect_specifier = ModuleSpecifier::from(redirect_url); - file_fetcher - .fetch_remote(&redirect_specifier, &permissions, redirect_limit - 1) + .fetch_remote(&redirect_url, &permissions, redirect_limit - 1) .await } FetchOnceResult::Code(bytes, headers) => { - file_fetcher.http_cache.set( - specifier.as_url(), - headers.clone(), - &bytes, - )?; + file_fetcher + .http_cache + .set(&specifier, headers.clone(), &bytes)?; let file = file_fetcher.build_remote_file(&specifier, bytes, &headers)?; Ok(file) @@ -587,7 +579,7 @@ impl FileFetcher { pub fn get_source(&self, specifier: &ModuleSpecifier) -> Option { let maybe_file = self.cache.get(specifier); if maybe_file.is_none() { - let is_local = specifier.as_url().scheme() == "file"; + let is_local = specifier.scheme() == "file"; if is_local { if let Ok(file) = fetch_local(specifier) { return Some(file); @@ -609,6 +601,8 @@ impl FileFetcher { mod tests { use super::*; use deno_core::error::get_custom_error_class; + use deno_core::resolve_url; + use deno_core::resolve_url_or_path; use std::rc::Rc; use tempfile::TempDir; @@ -654,7 +648,7 @@ mod tests { .fetch_remote(specifier, &Permissions::allow_all(), 1) .await; assert!(result.is_ok()); - let (_, headers) = file_fetcher.http_cache.get(specifier.as_url()).unwrap(); + let (_, headers) = file_fetcher.http_cache.get(specifier).unwrap(); (result.unwrap(), headers) } @@ -665,7 +659,7 @@ mod tests { ) { let url_str = format!("http://127.0.0.1:4545/cli/tests/encoding/{}", fixture); - let specifier = ModuleSpecifier::resolve_url(&url_str).unwrap(); + let specifier = resolve_url(&url_str).unwrap(); let (file, headers) = test_fetch_remote(&specifier).await; assert_eq!(file.source, expected); assert_eq!(file.media_type, MediaType::TypeScript); @@ -678,8 +672,7 @@ mod tests { async fn test_fetch_local_encoded(charset: &str, expected: String) { let p = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")) .join(format!("tests/encoding/{}.ts", charset)); - let specifier = - ModuleSpecifier::resolve_url_or_path(p.to_str().unwrap()).unwrap(); + let specifier = resolve_url_or_path(p.to_str().unwrap()).unwrap(); let (file, _) = test_fetch(&specifier).await; assert_eq!(file.source, expected); } @@ -704,7 +697,7 @@ mod tests { expected, ) in fixtures { - let specifier = ModuleSpecifier::resolve_url(url_str).unwrap(); + let specifier = resolve_url(url_str).unwrap(); let actual = get_source_from_data_url(&specifier); assert_eq!(actual.is_ok(), expected_ok); if expected_ok { @@ -730,7 +723,7 @@ mod tests { ]; for (specifier, is_ok, expected) in fixtures { - let specifier = ModuleSpecifier::resolve_url_or_path(specifier).unwrap(); + let specifier = resolve_url_or_path(specifier).unwrap(); let actual = get_validated_scheme(&specifier); assert_eq!(actual.is_ok(), is_ok); if is_ok { @@ -909,7 +902,7 @@ mod tests { ]; for (specifier, maybe_content_type, media_type, maybe_charset) in fixtures { - let specifier = ModuleSpecifier::resolve_url_or_path(specifier).unwrap(); + let specifier = resolve_url_or_path(specifier).unwrap(); assert_eq!( map_content_type(&specifier, maybe_content_type), (media_type, maybe_charset) @@ -922,8 +915,7 @@ mod tests { let (file_fetcher, temp_dir) = setup(CacheSetting::Use, None); let local = temp_dir.path().join("a.ts"); let specifier = - ModuleSpecifier::resolve_url_or_path(local.as_os_str().to_str().unwrap()) - .unwrap(); + resolve_url_or_path(local.as_os_str().to_str().unwrap()).unwrap(); let file = File { local, maybe_types: None, @@ -945,7 +937,7 @@ mod tests { async fn test_get_source() { let _http_server_guard = test_util::http_server(); let (file_fetcher, _) = setup(CacheSetting::Use, None); - let specifier = ModuleSpecifier::resolve_url( + let specifier = resolve_url( "http://localhost:4548/cli/tests/subdir/redirects/redirect1.js", ) .unwrap(); @@ -961,7 +953,7 @@ mod tests { assert_eq!(file.source, "export const redirect = 1;\n"); assert_eq!( file.specifier, - ModuleSpecifier::resolve_url( + resolve_url( "http://localhost:4545/cli/tests/subdir/redirects/redirect1.js" ) .unwrap() @@ -979,7 +971,7 @@ mod tests { #[tokio::test] async fn test_fetch_data_url() { let (file_fetcher, _) = setup(CacheSetting::Use, None); - let specifier = ModuleSpecifier::resolve_url("data:application/typescript;base64,ZXhwb3J0IGNvbnN0IGEgPSAiYSI7CgpleHBvcnQgZW51bSBBIHsKICBBLAogIEIsCiAgQywKfQo=").unwrap(); + let specifier = resolve_url("data:application/typescript;base64,ZXhwb3J0IGNvbnN0IGEgPSAiYSI7CgpleHBvcnQgZW51bSBBIHsKICBBLAogIEIsCiAgQywKfQo=").unwrap(); let result = file_fetcher .fetch(&specifier, &Permissions::allow_all()) @@ -1001,10 +993,9 @@ mod tests { let (file_fetcher, temp_dir) = setup(CacheSetting::Use, None); let (file_fetcher_01, _) = setup(CacheSetting::Use, Some(temp_dir.clone())); let (file_fetcher_02, _) = setup(CacheSetting::Use, Some(temp_dir.clone())); - let specifier = ModuleSpecifier::resolve_url_or_path( - "http://localhost:4545/cli/tests/subdir/mod2.ts", - ) - .unwrap(); + let specifier = + resolve_url_or_path("http://localhost:4545/cli/tests/subdir/mod2.ts") + .unwrap(); let result = file_fetcher .fetch(&specifier, &Permissions::allow_all()) @@ -1019,7 +1010,7 @@ mod tests { let cache_filename = file_fetcher .http_cache - .get_cache_filename(specifier.as_url()) + .get_cache_filename(&specifier) .unwrap(); let mut metadata = crate::http_cache::Metadata::read(&cache_filename).unwrap(); @@ -1042,8 +1033,7 @@ mod tests { // the value above. assert_eq!(file.media_type, MediaType::JavaScript); - let (_, headers) = - file_fetcher_02.http_cache.get(specifier.as_url()).unwrap(); + let (_, headers) = file_fetcher_02.http_cache.get(&specifier).unwrap(); assert_eq!(headers.get("content-type").unwrap(), "text/javascript"); metadata.headers = HashMap::new(); metadata @@ -1098,13 +1088,12 @@ mod tests { None, ) .expect("could not create file fetcher"); - let specifier = ModuleSpecifier::resolve_url( - "http://localhost:4545/cli/tests/subdir/mismatch_ext.ts", - ) - .unwrap(); + let specifier = + resolve_url("http://localhost:4545/cli/tests/subdir/mismatch_ext.ts") + .unwrap(); let cache_filename = file_fetcher_01 .http_cache - .get_cache_filename(specifier.as_url()) + .get_cache_filename(&specifier) .unwrap(); let result = file_fetcher_01 @@ -1148,21 +1137,21 @@ mod tests { async fn test_fetch_redirected() { let _http_server_guard = test_util::http_server(); let (file_fetcher, _) = setup(CacheSetting::Use, None); - let specifier = ModuleSpecifier::resolve_url( + let specifier = resolve_url( "http://localhost:4546/cli/tests/subdir/redirects/redirect1.js", ) .unwrap(); let cached_filename = file_fetcher .http_cache - .get_cache_filename(specifier.as_url()) + .get_cache_filename(&specifier) .unwrap(); - let redirected_specifier = ModuleSpecifier::resolve_url( + let redirected_specifier = resolve_url( "http://localhost:4545/cli/tests/subdir/redirects/redirect1.js", ) .unwrap(); let redirected_cached_filename = file_fetcher .http_cache - .get_cache_filename(redirected_specifier.as_url()) + .get_cache_filename(&redirected_specifier) .unwrap(); let result = file_fetcher @@ -1179,7 +1168,7 @@ mod tests { ); let (_, headers) = file_fetcher .http_cache - .get(specifier.as_url()) + .get(&specifier) .expect("could not get file"); assert_eq!( headers.get("location").unwrap(), @@ -1192,7 +1181,7 @@ mod tests { ); let (_, headers) = file_fetcher .http_cache - .get(redirected_specifier.as_url()) + .get(&redirected_specifier) .expect("could not get file"); assert!(headers.get("location").is_none()); } @@ -1201,29 +1190,29 @@ mod tests { async fn test_fetch_multiple_redirects() { let _http_server_guard = test_util::http_server(); let (file_fetcher, _) = setup(CacheSetting::Use, None); - let specifier = ModuleSpecifier::resolve_url( + let specifier = resolve_url( "http://localhost:4548/cli/tests/subdir/redirects/redirect1.js", ) .unwrap(); let cached_filename = file_fetcher .http_cache - .get_cache_filename(specifier.as_url()) + .get_cache_filename(&specifier) .unwrap(); - let redirected_01_specifier = ModuleSpecifier::resolve_url( + let redirected_01_specifier = resolve_url( "http://localhost:4546/cli/tests/subdir/redirects/redirect1.js", ) .unwrap(); let redirected_01_cached_filename = file_fetcher .http_cache - .get_cache_filename(redirected_01_specifier.as_url()) + .get_cache_filename(&redirected_01_specifier) .unwrap(); - let redirected_02_specifier = ModuleSpecifier::resolve_url( + let redirected_02_specifier = resolve_url( "http://localhost:4545/cli/tests/subdir/redirects/redirect1.js", ) .unwrap(); let redirected_02_cached_filename = file_fetcher .http_cache - .get_cache_filename(redirected_02_specifier.as_url()) + .get_cache_filename(&redirected_02_specifier) .unwrap(); let result = file_fetcher @@ -1240,7 +1229,7 @@ mod tests { ); let (_, headers) = file_fetcher .http_cache - .get(specifier.as_url()) + .get(&specifier) .expect("could not get file"); assert_eq!( headers.get("location").unwrap(), @@ -1254,7 +1243,7 @@ mod tests { ); let (_, headers) = file_fetcher .http_cache - .get(redirected_01_specifier.as_url()) + .get(&redirected_01_specifier) .expect("could not get file"); assert_eq!( headers.get("location").unwrap(), @@ -1267,7 +1256,7 @@ mod tests { ); let (_, headers) = file_fetcher .http_cache - .get(redirected_02_specifier.as_url()) + .get(&redirected_02_specifier) .expect("could not get file"); assert!(headers.get("location").is_none()); } @@ -1286,17 +1275,15 @@ mod tests { None, ) .expect("could not create file fetcher"); - let specifier = ModuleSpecifier::resolve_url( - "http://localhost:4548/cli/tests/subdir/mismatch_ext.ts", - ) - .unwrap(); - let redirected_specifier = ModuleSpecifier::resolve_url( - "http://localhost:4546/cli/tests/subdir/mismatch_ext.ts", - ) - .unwrap(); + let specifier = + resolve_url("http://localhost:4548/cli/tests/subdir/mismatch_ext.ts") + .unwrap(); + let redirected_specifier = + resolve_url("http://localhost:4546/cli/tests/subdir/mismatch_ext.ts") + .unwrap(); let redirected_cache_filename = file_fetcher_01 .http_cache - .get_cache_filename(redirected_specifier.as_url()) + .get_cache_filename(&redirected_specifier) .unwrap(); let result = file_fetcher_01 @@ -1340,7 +1327,7 @@ mod tests { async fn test_fetcher_limits_redirects() { let _http_server_guard = test_util::http_server(); let (file_fetcher, _) = setup(CacheSetting::Use, None); - let specifier = ModuleSpecifier::resolve_url( + let specifier = resolve_url( "http://localhost:4548/cli/tests/subdir/redirects/redirect1.js", ) .unwrap(); @@ -1366,21 +1353,21 @@ mod tests { async fn test_fetch_same_host_redirect() { let _http_server_guard = test_util::http_server(); let (file_fetcher, _) = setup(CacheSetting::Use, None); - let specifier = ModuleSpecifier::resolve_url( + let specifier = resolve_url( "http://localhost:4550/REDIRECT/cli/tests/subdir/redirects/redirect1.js", ) .unwrap(); let cached_filename = file_fetcher .http_cache - .get_cache_filename(specifier.as_url()) + .get_cache_filename(&specifier) .unwrap(); - let redirected_specifier = ModuleSpecifier::resolve_url( + let redirected_specifier = resolve_url( "http://localhost:4550/cli/tests/subdir/redirects/redirect1.js", ) .unwrap(); let redirected_cached_filename = file_fetcher .http_cache - .get_cache_filename(redirected_specifier.as_url()) + .get_cache_filename(&redirected_specifier) .unwrap(); let result = file_fetcher @@ -1397,7 +1384,7 @@ mod tests { ); let (_, headers) = file_fetcher .http_cache - .get(specifier.as_url()) + .get(&specifier) .expect("could not get file"); assert_eq!( headers.get("location").unwrap(), @@ -1410,7 +1397,7 @@ mod tests { ); let (_, headers) = file_fetcher .http_cache - .get(redirected_specifier.as_url()) + .get(&redirected_specifier) .expect("could not get file"); assert!(headers.get("location").is_none()); } @@ -1427,10 +1414,8 @@ mod tests { None, ) .expect("could not create file fetcher"); - let specifier = ModuleSpecifier::resolve_url( - "http://localhost:4545/cli/tests/002_hello.ts", - ) - .unwrap(); + let specifier = + resolve_url("http://localhost:4545/cli/tests/002_hello.ts").unwrap(); let result = file_fetcher .fetch(&specifier, &Permissions::allow_all()) @@ -1462,10 +1447,8 @@ mod tests { None, ) .expect("could not create file fetcher"); - let specifier = ModuleSpecifier::resolve_url( - "http://localhost:4545/cli/tests/002_hello.ts", - ) - .unwrap(); + let specifier = + resolve_url("http://localhost:4545/cli/tests/002_hello.ts").unwrap(); let result = file_fetcher_01 .fetch(&specifier, &Permissions::allow_all()) @@ -1495,8 +1478,7 @@ mod tests { let (file_fetcher, temp_dir) = setup(CacheSetting::Use, None); let fixture_path = temp_dir.path().join("mod.ts"); let specifier = - ModuleSpecifier::resolve_url_or_path(&fixture_path.to_string_lossy()) - .unwrap(); + resolve_url_or_path(&fixture_path.to_string_lossy()).unwrap(); fs::write(fixture_path.clone(), r#"console.log("hello deno");"#) .expect("could not write file"); let result = file_fetcher @@ -1545,10 +1527,8 @@ mod tests { #[tokio::test] async fn test_fetch_remote_with_types() { - let specifier = ModuleSpecifier::resolve_url_or_path( - "http://127.0.0.1:4545/xTypeScriptTypes.js", - ) - .unwrap(); + let specifier = + resolve_url_or_path("http://127.0.0.1:4545/xTypeScriptTypes.js").unwrap(); let (file, _) = test_fetch_remote(&specifier).await; assert_eq!( file.maybe_types, diff --git a/cli/import_map.rs b/cli/import_map.rs index a71a4f405e..ab52de1a92 100644 --- a/cli/import_map.rs +++ b/cli/import_map.rs @@ -172,7 +172,7 @@ impl ImportMap { continue; } - normalized_addresses.push(url.into()); + normalized_addresses.push(url); } normalized_addresses @@ -367,13 +367,13 @@ impl ImportMap { if address_vec.is_empty() { return Err(ImportMapError::new(&format!("Specifier {:?} was mapped to no addresses (via prefix specifier key {:?}).", normalized_specifier, specifier_key))); } else if address_vec.len() == 1 { - let address = address_vec.first().unwrap(); + let base_url = address_vec.first().unwrap(); let after_prefix = &normalized_specifier[specifier_key.len()..]; - let base_url = address.as_url(); if let Ok(url) = base_url.join(after_prefix) { - debug!("Specifier {:?} was mapped to {:?} (via prefix specifier key {:?}).", normalized_specifier, url, address); - return Ok(Some(ModuleSpecifier::from(url))); + debug!("Specifier {:?} was mapped to {:?} (via prefix specifier key {:?}).", + normalized_specifier, url, base_url); + return Ok(Some(url)); } unreachable!(); @@ -434,7 +434,7 @@ impl ImportMap { // no match in import map but we got resolvable URL if let Some(resolved_url) = resolved_url { - return Ok(Some(ModuleSpecifier::from(resolved_url))); + return Ok(Some(resolved_url)); } Err(ImportMapError::new(&format!( @@ -513,18 +513,18 @@ mod tests { .imports .get("https://base.example/path1/path2/foo") .unwrap()[0], - "https://base.example/dotslash".to_string() + Url::parse("https://base.example/dotslash").unwrap() ); assert_eq!( import_map .imports .get("https://base.example/path1/foo") .unwrap()[0], - "https://base.example/dotdotslash".to_string() + Url::parse("https://base.example/dotdotslash").unwrap() ); assert_eq!( import_map.imports.get("https://base.example/foo").unwrap()[0], - "https://base.example/slash".to_string() + Url::parse("https://base.example/slash").unwrap() ); // Should absolutize the literal strings ./, ../, or / with no suffix.. @@ -543,18 +543,18 @@ mod tests { .imports .get("https://base.example/path1/path2/") .unwrap()[0], - "https://base.example/dotslash/".to_string() + Url::parse("https://base.example/dotslash/").unwrap() ); assert_eq!( import_map .imports .get("https://base.example/path1/") .unwrap()[0], - "https://base.example/dotdotslash/".to_string() + Url::parse("https://base.example/dotdotslash/").unwrap() ); assert_eq!( import_map.imports.get("https://base.example/").unwrap()[0], - "https://base.example/slash/".to_string() + Url::parse("https://base.example/slash/").unwrap() ); // Should treat percent-encoded variants of ./, ../, or / as bare specifiers.. @@ -574,31 +574,31 @@ mod tests { .unwrap(); assert_eq!( import_map.imports.get("%2E/").unwrap()[0], - "https://base.example/dotSlash1/".to_string() + Url::parse("https://base.example/dotSlash1/").unwrap() ); assert_eq!( import_map.imports.get("%2E%2E/").unwrap()[0], - "https://base.example/dotDotSlash1/".to_string() + Url::parse("https://base.example/dotDotSlash1/").unwrap() ); assert_eq!( import_map.imports.get(".%2F").unwrap()[0], - "https://base.example/dotSlash2".to_string() + Url::parse("https://base.example/dotSlash2").unwrap() ); assert_eq!( import_map.imports.get("..%2F").unwrap()[0], - "https://base.example/dotDotSlash2".to_string() + Url::parse("https://base.example/dotDotSlash2").unwrap() ); assert_eq!( import_map.imports.get("%2F").unwrap()[0], - "https://base.example/slash2".to_string() + Url::parse("https://base.example/slash2").unwrap() ); assert_eq!( import_map.imports.get("%2E%2F").unwrap()[0], - "https://base.example/dotSlash3".to_string() + Url::parse("https://base.example/dotSlash3").unwrap() ); assert_eq!( import_map.imports.get("%2E%2E%2F").unwrap()[0], - "https://base.example/dotDotSlash3".to_string() + Url::parse("https://base.example/dotDotSlash3").unwrap() ); } @@ -627,47 +627,47 @@ mod tests { .unwrap(); assert_eq!( import_map.imports.get("http://good/").unwrap()[0], - "https://base.example/http/".to_string() + Url::parse("https://base.example/http/").unwrap() ); assert_eq!( import_map.imports.get("https://good/").unwrap()[0], - "https://base.example/https/".to_string() + Url::parse("https://base.example/https/").unwrap() ); assert_eq!( import_map.imports.get("file:///good").unwrap()[0], - "https://base.example/file".to_string() + Url::parse("https://base.example/file").unwrap() ); assert_eq!( import_map.imports.get("http://good/").unwrap()[0], - "https://base.example/http/".to_string() + Url::parse("https://base.example/http/").unwrap() ); assert_eq!( import_map.imports.get("import:bad").unwrap()[0], - "https://base.example/import".to_string() + Url::parse("https://base.example/import").unwrap() ); assert_eq!( import_map.imports.get("mailto:bad").unwrap()[0], - "https://base.example/mailto".to_string() + Url::parse("https://base.example/mailto").unwrap() ); assert_eq!( import_map.imports.get("javascript:bad").unwrap()[0], - "https://base.example/javascript".to_string() + Url::parse("https://base.example/javascript").unwrap() ); assert_eq!( import_map.imports.get("wss:bad").unwrap()[0], - "https://base.example/wss".to_string() + Url::parse("https://base.example/wss").unwrap() ); assert_eq!( import_map.imports.get("about:bad").unwrap()[0], - "https://base.example/about".to_string() + Url::parse("https://base.example/about").unwrap() ); assert_eq!( import_map.imports.get("blob:bad").unwrap()[0], - "https://base.example/blob".to_string() + Url::parse("https://base.example/blob").unwrap() ); assert_eq!( import_map.imports.get("data:bad").unwrap()[0], - "https://base.example/data".to_string() + Url::parse("https://base.example/data").unwrap() ); // Should parse absolute URLs, treating unparseable ones as bare specifiers.. @@ -688,35 +688,35 @@ mod tests { .unwrap(); assert_eq!( import_map.imports.get("https://ex ample.org/").unwrap()[0], - "https://base.example/unparseable1/".to_string() + Url::parse("https://base.example/unparseable1/").unwrap() ); assert_eq!( import_map.imports.get("https://example.com:demo").unwrap()[0], - "https://base.example/unparseable2".to_string() + Url::parse("https://base.example/unparseable2").unwrap() ); assert_eq!( import_map.imports.get("http://[www.example.com]/").unwrap()[0], - "https://base.example/unparseable3/".to_string() + Url::parse("https://base.example/unparseable3/").unwrap() ); assert_eq!( import_map.imports.get("https://example.org/").unwrap()[0], - "https://base.example/invalidButParseable1/".to_string() + Url::parse("https://base.example/invalidButParseable1/").unwrap() ); assert_eq!( import_map.imports.get("https://example.com///").unwrap()[0], - "https://base.example/invalidButParseable2/".to_string() + Url::parse("https://base.example/invalidButParseable2/").unwrap() ); assert_eq!( import_map.imports.get("https://example.net/").unwrap()[0], - "https://base.example/prettyNormal/".to_string() + Url::parse("https://base.example/prettyNormal/").unwrap() ); assert_eq!( import_map.imports.get("https://example.com/").unwrap()[0], - "https://base.example/percentDecoding/".to_string() + Url::parse("https://base.example/percentDecoding/").unwrap() ); assert_eq!( import_map.imports.get("https://example.com/%41").unwrap()[0], - "https://base.example/noPercentDecoding".to_string() + Url::parse("https://base.example/noPercentDecoding").unwrap() ); } @@ -893,15 +893,15 @@ mod tests { assert_eq!( import_map.imports.get("dotSlash").unwrap(), - &vec!["https://base.example/path1/path2/foo".to_string()] + &vec![Url::parse("https://base.example/path1/path2/foo").unwrap()] ); assert_eq!( import_map.imports.get("dotDotSlash").unwrap(), - &vec!["https://base.example/path1/foo".to_string()] + &vec![Url::parse("https://base.example/path1/foo").unwrap()] ); assert_eq!( import_map.imports.get("slash").unwrap(), - &vec!["https://base.example/foo".to_string()] + &vec![Url::parse("https://base.example/foo").unwrap()] ); // Should accept the literal strings ./, ../, or / with no suffix.. @@ -918,15 +918,15 @@ mod tests { assert_eq!( import_map.imports.get("dotSlash").unwrap(), - &vec!["https://base.example/path1/path2/".to_string()] + &vec![Url::parse("https://base.example/path1/path2/").unwrap()] ); assert_eq!( import_map.imports.get("dotDotSlash").unwrap(), - &vec!["https://base.example/path1/".to_string()] + &vec![Url::parse("https://base.example/path1/").unwrap()] ); assert_eq!( import_map.imports.get("slash").unwrap(), - &vec!["https://base.example/".to_string()] + &vec![Url::parse("https://base.example/").unwrap()] ); // Should ignore percent-encoded variants of ./, ../, or /.. @@ -979,19 +979,19 @@ mod tests { assert_eq!( import_map.imports.get("file").unwrap(), - &vec!["file:///good".to_string()] + &vec![Url::parse("file:///good").unwrap()] ); assert_eq!( import_map.imports.get("http").unwrap(), - &vec!["http://good/".to_string()] + &vec![Url::parse("http://good/").unwrap()] ); assert_eq!( import_map.imports.get("https").unwrap(), - &vec!["https://good/".to_string()] + &vec![Url::parse("https://good/").unwrap()] ); assert_eq!( import_map.imports.get("data").unwrap(), - &vec!["data:good".to_string()] + &vec![Url::parse("data:good").unwrap()] ); assert!(import_map.imports.get("about").unwrap().is_empty()); @@ -1029,19 +1029,19 @@ mod tests { assert_eq!( import_map.imports.get("file").unwrap(), - &vec!["file:///good".to_string()] + &vec![Url::parse("file:///good").unwrap()] ); assert_eq!( import_map.imports.get("http").unwrap(), - &vec!["http://good/".to_string()] + &vec![Url::parse("http://good/").unwrap()] ); assert_eq!( import_map.imports.get("https").unwrap(), - &vec!["https://good/".to_string()] + &vec![Url::parse("https://good/").unwrap()] ); assert_eq!( import_map.imports.get("data").unwrap(), - &vec!["data:good".to_string()] + &vec![Url::parse("data:good").unwrap()] ); assert!(import_map.imports.get("about").unwrap().is_empty()); @@ -1075,23 +1075,23 @@ mod tests { assert_eq!( import_map.imports.get("invalidButParseable1").unwrap(), - &vec!["https://example.org/".to_string()] + &vec![Url::parse("https://example.org/").unwrap()] ); assert_eq!( import_map.imports.get("invalidButParseable2").unwrap(), - &vec!["https://example.com///".to_string()] + &vec![Url::parse("https://example.com///").unwrap()] ); assert_eq!( import_map.imports.get("prettyNormal").unwrap(), - &vec!["https://example.net/".to_string()] + &vec![Url::parse("https://example.net/").unwrap()] ); assert_eq!( import_map.imports.get("percentDecoding").unwrap(), - &vec!["https://example.com/".to_string()] + &vec![Url::parse("https://example.com/").unwrap()] ); assert_eq!( import_map.imports.get("noPercentDecoding").unwrap(), - &vec!["https://example.com/%41".to_string()] + &vec![Url::parse("https://example.com/%41").unwrap()] ); assert!(import_map.imports.get("unparseable1").unwrap().is_empty()); @@ -1120,23 +1120,23 @@ mod tests { assert_eq!( import_map.imports.get("invalidButParseable1").unwrap(), - &vec!["https://example.org/".to_string()] + &vec![Url::parse("https://example.org/").unwrap()] ); assert_eq!( import_map.imports.get("invalidButParseable2").unwrap(), - &vec!["https://example.com///".to_string()] + &vec![Url::parse("https://example.com///").unwrap()] ); assert_eq!( import_map.imports.get("prettyNormal").unwrap(), - &vec!["https://example.net/".to_string()] + &vec![Url::parse("https://example.net/").unwrap()] ); assert_eq!( import_map.imports.get("percentDecoding").unwrap(), - &vec!["https://example.com/".to_string()] + &vec![Url::parse("https://example.com/").unwrap()] ); assert_eq!( import_map.imports.get("noPercentDecoding").unwrap(), - &vec!["https://example.com/%41".to_string()] + &vec![Url::parse("https://example.com/%41").unwrap()] ); assert!(import_map.imports.get("unparseable1").unwrap().is_empty()); @@ -1190,7 +1190,7 @@ mod tests { assert_eq!( import_map.imports.get("trailer/").unwrap(), - &vec!["https://base.example/atrailer/".to_string()] + &vec![Url::parse("https://base.example/atrailer/").unwrap()] ); // TODO: I'd be good to assert that warning was shown } @@ -1230,7 +1230,7 @@ mod tests { .unwrap_or_else(|err| panic!("ImportMap::resolve failed: {:?}", err)); let resolved_url = maybe_url.unwrap_or_else(|| panic!("Unexpected None resolved URL")); - assert_eq!(resolved_url, expected_url.to_string()); + assert_eq!(resolved_url.as_str(), expected_url); } #[test] diff --git a/cli/info.rs b/cli/info.rs index 1f3a0096a4..61daa93412 100644 --- a/cli/info.rs +++ b/cli/info.rs @@ -202,6 +202,7 @@ pub fn human_size(size: f64) -> String { #[cfg(test)] mod test { use super::*; + use deno_core::resolve_url_or_path; use deno_core::serde_json::json; #[test] @@ -218,12 +219,8 @@ mod test { } fn get_fixture() -> ModuleGraphInfo { - let spec_c = - ModuleSpecifier::resolve_url_or_path("https://deno.land/x/a/b/c.ts") - .unwrap(); - let spec_d = - ModuleSpecifier::resolve_url_or_path("https://deno.land/x/a/b/c.ts") - .unwrap(); + let spec_c = resolve_url_or_path("https://deno.land/x/a/b/c.ts").unwrap(); + let spec_d = resolve_url_or_path("https://deno.land/x/a/b/c.ts").unwrap(); let deps = vec![ModuleInfo { deps: Vec::new(), name: spec_d.clone(), @@ -261,10 +258,7 @@ mod test { info, local: PathBuf::from("/a/b/c.ts"), map: None, - module: ModuleSpecifier::resolve_url_or_path( - "https://deno.land/x/a/b/c.ts", - ) - .unwrap(), + module: resolve_url_or_path("https://deno.land/x/a/b/c.ts").unwrap(), total_size: 999999, } } diff --git a/cli/lsp/analysis.rs b/cli/lsp/analysis.rs index 1824e52988..8a1c565376 100644 --- a/cli/lsp/analysis.rs +++ b/cli/lsp/analysis.rs @@ -211,7 +211,7 @@ pub fn resolve_import( let specifier = if let Some(remapped) = maybe_mapped { remapped } else { - match ModuleSpecifier::resolve_import(specifier, referrer.as_str()) { + match deno_core::resolve_import(specifier, referrer.as_str()) { Ok(resolved) => resolved, Err(err) => { return ResolvedDependency::Err( @@ -220,8 +220,8 @@ pub fn resolve_import( } } }; - let referrer_scheme = referrer.as_url().scheme(); - let specifier_scheme = specifier.as_url().scheme(); + let referrer_scheme = referrer.scheme(); + let specifier_scheme = specifier.scheme(); if referrer_scheme == "https" && specifier_scheme == "http" { return ResolvedDependency::Err(ResolvedDependencyErr::InvalidDowngrade); } @@ -647,6 +647,7 @@ impl CodeActionCollection { #[cfg(test)] mod tests { use super::*; + use deno_core::resolve_url; #[test] fn test_as_lsp_range() { @@ -680,8 +681,7 @@ mod tests { #[test] fn test_analyze_dependencies() { - let specifier = - ModuleSpecifier::resolve_url("file:///a.ts").expect("bad specifier"); + let specifier = resolve_url("file:///a.ts").expect("bad specifier"); let source = r#"import { Application, Context, @@ -703,14 +703,10 @@ mod tests { Some(Dependency { is_dynamic: false, maybe_code: Some(ResolvedDependency::Resolved( - ModuleSpecifier::resolve_url("https://cdn.skypack.dev/react") - .unwrap() + resolve_url("https://cdn.skypack.dev/react").unwrap() )), maybe_type: Some(ResolvedDependency::Resolved( - ModuleSpecifier::resolve_url( - "https://deno.land/x/types/react/index.d.ts" - ) - .unwrap() + resolve_url("https://deno.land/x/types/react/index.d.ts").unwrap() )), maybe_code_specifier_range: Some(Range { start: Position { @@ -729,8 +725,7 @@ mod tests { Some(Dependency { is_dynamic: false, maybe_code: Some(ResolvedDependency::Resolved( - ModuleSpecifier::resolve_url("https://deno.land/x/oak@v6.3.2/mod.ts") - .unwrap() + resolve_url("https://deno.land/x/oak@v6.3.2/mod.ts").unwrap() )), maybe_type: None, maybe_code_specifier_range: Some(Range { diff --git a/cli/lsp/diagnostics.rs b/cli/lsp/diagnostics.rs index 1237ff7a34..8017d7e28c 100644 --- a/cli/lsp/diagnostics.rs +++ b/cli/lsp/diagnostics.rs @@ -248,7 +248,7 @@ pub async fn generate_ts_diagnostics( let res = ts_server.request(state_snapshot.clone(), req).await?; let ts_diagnostic_map: TsDiagnostics = serde_json::from_value(res)?; for (specifier_str, ts_diagnostics) in ts_diagnostic_map.iter() { - let specifier = ModuleSpecifier::resolve_url(specifier_str)?; + let specifier = deno_core::resolve_url(specifier_str)?; let version = state_snapshot.documents.version(&specifier); diagnostics.push(( specifier, @@ -295,7 +295,7 @@ pub async fn generate_dependency_diagnostics( } ResolvedDependency::Resolved(specifier) => { if !(state_snapshot.documents.contains_key(&specifier) || sources.contains_key(&specifier)) { - let is_local = specifier.as_url().scheme() == "file"; + let is_local = specifier.scheme() == "file"; let (code, message) = if is_local { (Some(lsp::NumberOrString::String("no-local".to_string())), format!("Unable to load a local module: \"{}\".\n Please check the file path.", specifier)) } else { diff --git a/cli/lsp/documents.rs b/cli/lsp/documents.rs index 2a99069586..ef90610041 100644 --- a/cli/lsp/documents.rs +++ b/cli/lsp/documents.rs @@ -204,14 +204,14 @@ impl DocumentCache { #[cfg(test)] mod tests { use super::*; + use deno_core::resolve_url; use lspower::lsp; #[test] fn test_document_cache_contains() { let mut document_cache = DocumentCache::default(); - let specifier = ModuleSpecifier::resolve_url("file:///a/b.ts").unwrap(); - let missing_specifier = - ModuleSpecifier::resolve_url("file:///a/c.ts").unwrap(); + let specifier = resolve_url("file:///a/b.ts").unwrap(); + let missing_specifier = resolve_url("file:///a/c.ts").unwrap(); document_cache.open(specifier.clone(), 1, "console.log(\"Hello Deno\");\n"); assert!(document_cache.contains_key(&specifier)); assert!(!document_cache.contains_key(&missing_specifier)); @@ -220,7 +220,7 @@ mod tests { #[test] fn test_document_cache_change() { let mut document_cache = DocumentCache::default(); - let specifier = ModuleSpecifier::resolve_url("file:///a/b.ts").unwrap(); + let specifier = resolve_url("file:///a/b.ts").unwrap(); document_cache.open(specifier.clone(), 1, "console.log(\"Hello deno\");\n"); document_cache .change( @@ -251,7 +251,7 @@ mod tests { #[test] fn test_document_cache_change_utf16() { let mut document_cache = DocumentCache::default(); - let specifier = ModuleSpecifier::resolve_url("file:///a/b.ts").unwrap(); + let specifier = resolve_url("file:///a/b.ts").unwrap(); document_cache.open(specifier.clone(), 1, "console.log(\"Hello 🦕\");\n"); document_cache .change( diff --git a/cli/lsp/language_server.rs b/cli/lsp/language_server.rs index 1501249e84..8de90607f1 100644 --- a/cli/lsp/language_server.rs +++ b/cli/lsp/language_server.rs @@ -2,6 +2,7 @@ use deno_core::error::anyhow; use deno_core::error::AnyError; +use deno_core::resolve_url; use deno_core::serde::Deserialize; use deno_core::serde::Serialize; use deno_core::serde_json; @@ -172,7 +173,7 @@ impl Inner { specifier: ModuleSpecifier, ) -> Result { let mark = self.performance.mark("get_line_index"); - let result = if specifier.as_url().scheme() == "asset" { + let result = if specifier.scheme() == "asset" { if let Some(asset) = self.get_asset(&specifier).await? { Ok(asset.line_index) } else { @@ -196,7 +197,7 @@ impl Inner { specifier: &ModuleSpecifier, ) -> Option { let mark = self.performance.mark("get_line_index_sync"); - let maybe_line_index = if specifier.as_url().scheme() == "asset" { + let maybe_line_index = if specifier.scheme() == "asset" { if let Some(Some(asset)) = self.assets.get(specifier) { Some(asset.line_index.clone()) } else { @@ -374,7 +375,7 @@ impl Inner { .cloned(), ); } - let uri = specifier.as_url().clone(); + let uri = specifier.clone(); let version = self.documents.version(&specifier); self .client @@ -1200,7 +1201,7 @@ impl Inner { if let Some(implementations) = maybe_implementations { let mut locations = Vec::new(); for implementation in implementations { - let implementation_specifier = ModuleSpecifier::resolve_url( + let implementation_specifier = resolve_url( &implementation.document_span.file_name, ) .map_err(|err| { @@ -1285,7 +1286,7 @@ impl Inner { if reference.is_definition { continue; } - let reference_specifier = ModuleSpecifier::resolve_url( + let reference_specifier = resolve_url( &reference.document_span.file_name, ) .map_err(|err| { @@ -1439,8 +1440,7 @@ impl Inner { continue; } let reference_specifier = - ModuleSpecifier::resolve_url(&reference.document_span.file_name) - .unwrap(); + resolve_url(&reference.document_span.file_name).unwrap(); // TODO(lucacasonato): handle error correctly let line_index = self.get_line_index(reference_specifier).await.unwrap(); @@ -1585,8 +1585,7 @@ impl Inner { let mut results = Vec::new(); for impl_ in implementations { let document_span = impl_.document_span; - let impl_specifier = - ModuleSpecifier::resolve_url(&document_span.file_name).unwrap(); + let impl_specifier = resolve_url(&document_span.file_name).unwrap(); let impl_line_index = &self.get_line_index(impl_specifier).await.unwrap(); if let Some(link) = document_span.to_link(impl_line_index, self).await { @@ -1981,8 +1980,7 @@ impl Inner { ) -> LspResult> { let mark = self.performance.mark("virtual_text_document"); let specifier = utils::normalize_url(params.text_document.uri); - let url = specifier.as_url(); - let contents = if url.as_str() == "deno:/status.md" { + let contents = if specifier.as_str() == "deno:/status.md" { let mut contents = String::new(); contents.push_str(&format!( @@ -2001,7 +1999,7 @@ impl Inner { } Some(contents) } else { - match url.scheme() { + match specifier.scheme() { "asset" => { if let Some(asset) = self .get_asset(&specifier) diff --git a/cli/lsp/sources.rs b/cli/lsp/sources.rs index 2654918956..0cd7b5876d 100644 --- a/cli/lsp/sources.rs +++ b/cli/lsp/sources.rs @@ -55,12 +55,12 @@ fn resolve_remote_specifier( http_cache: &HttpCache, redirect_limit: isize, ) -> Option { - let cache_filename = http_cache.get_cache_filename(specifier.as_url())?; + let cache_filename = http_cache.get_cache_filename(specifier)?; if redirect_limit >= 0 && cache_filename.is_file() { let headers = get_remote_headers(&cache_filename)?; if let Some(location) = headers.get("location") { let redirect = - ModuleSpecifier::resolve_import(location, specifier.as_str()).ok()?; + deno_core::resolve_import(location, specifier.as_str()).ok()?; resolve_remote_specifier(&redirect, http_cache, redirect_limit - 1) } else { Some(specifier.clone()) @@ -75,7 +75,7 @@ fn resolve_specifier( redirects: &mut HashMap, http_cache: &HttpCache, ) -> Option { - let scheme = specifier.as_url().scheme(); + let scheme = specifier.scheme(); if !SUPPORTED_SCHEMES.contains(&scheme) { return None; } @@ -83,7 +83,7 @@ fn resolve_specifier( if scheme == "data" { Some(specifier.clone()) } else if scheme == "file" { - let path = specifier.as_url().to_file_path().ok()?; + let path = specifier.to_file_path().ok()?; if path.is_file() { Some(specifier.clone()) } else { @@ -295,15 +295,14 @@ impl Inner { let version = self.calculate_script_version(specifier)?; let path = self.get_path(specifier)?; let bytes = fs::read(path).ok()?; - let scheme = specifier.as_url().scheme(); + let scheme = specifier.scheme(); let (source, media_type, maybe_types) = if scheme == "file" { let maybe_charset = Some(text_encoding::detect_charset(&bytes).to_string()); let source = get_source_from_bytes(bytes, maybe_charset).ok()?; (source, MediaType::from(specifier), None) } else { - let cache_filename = - self.http_cache.get_cache_filename(specifier.as_url())?; + let cache_filename = self.http_cache.get_cache_filename(specifier)?; let headers = get_remote_headers(&cache_filename)?; let maybe_content_type = headers.get("content-type").cloned(); let (media_type, maybe_charset) = @@ -329,12 +328,12 @@ impl Inner { } fn get_path(&mut self, specifier: &ModuleSpecifier) -> Option { - if specifier.as_url().scheme() == "file" { - specifier.as_url().to_file_path().ok() + if specifier.scheme() == "file" { + specifier.to_file_path().ok() } else if let Some(path) = self.remotes.get(&specifier) { Some(path.clone()) } else { - let path = self.http_cache.get_cache_filename(&specifier.as_url())?; + let path = self.http_cache.get_cache_filename(&specifier)?; if path.is_file() { self.remotes.insert(specifier.clone(), path.clone()); Some(path) @@ -436,6 +435,8 @@ impl Inner { #[cfg(test)] mod tests { use super::*; + use deno_core::resolve_path; + use deno_core::resolve_url; use std::env; use tempfile::TempDir; @@ -451,10 +452,8 @@ mod tests { let (sources, _) = setup(); let c = PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").unwrap()); let tests = c.join("tests"); - let specifier = ModuleSpecifier::resolve_path( - &tests.join("001_hello.js").to_string_lossy(), - ) - .unwrap(); + let specifier = + resolve_path(&tests.join("001_hello.js").to_string_lossy()).unwrap(); let actual = sources.get_script_version(&specifier); assert!(actual.is_some()); } @@ -464,10 +463,8 @@ mod tests { let (sources, _) = setup(); let c = PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").unwrap()); let tests = c.join("tests"); - let specifier = ModuleSpecifier::resolve_path( - &tests.join("001_hello.js").to_string_lossy(), - ) - .unwrap(); + let specifier = + resolve_path(&tests.join("001_hello.js").to_string_lossy()).unwrap(); let actual = sources.get_source(&specifier); assert!(actual.is_some()); let actual = actual.unwrap(); @@ -479,10 +476,8 @@ mod tests { let (sources, _) = setup(); let c = PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").unwrap()); let tests = c.join("tests"); - let specifier = ModuleSpecifier::resolve_path( - &tests.join("001_hello.js").to_string_lossy(), - ) - .unwrap(); + let specifier = + resolve_path(&tests.join("001_hello.js").to_string_lossy()).unwrap(); let actual = sources.get_length_utf16(&specifier); assert!(actual.is_some()); let actual = actual.unwrap(); @@ -493,32 +488,25 @@ mod tests { fn test_resolve_dependency_types() { let (sources, location) = setup(); let cache = HttpCache::new(&location); - let specifier_dep = - ModuleSpecifier::resolve_url("https://deno.land/x/mod.ts").unwrap(); + let specifier_dep = resolve_url("https://deno.land/x/mod.ts").unwrap(); cache .set( - specifier_dep.as_url(), + &specifier_dep, Default::default(), b"export * from \"https://deno.land/x/lib.js\";", ) .unwrap(); - let specifier_code = - ModuleSpecifier::resolve_url("https://deno.land/x/lib.js").unwrap(); + let specifier_code = resolve_url("https://deno.land/x/lib.js").unwrap(); let mut headers_code = HashMap::new(); headers_code .insert("x-typescript-types".to_string(), "./lib.d.ts".to_string()); cache - .set( - specifier_code.as_url(), - headers_code, - b"export const a = 1;", - ) + .set(&specifier_code, headers_code, b"export const a = 1;") .unwrap(); - let specifier_type = - ModuleSpecifier::resolve_url("https://deno.land/x/lib.d.ts").unwrap(); + let specifier_type = resolve_url("https://deno.land/x/lib.d.ts").unwrap(); cache .set( - specifier_type.as_url(), + &specifier_type, Default::default(), b"export const a: number;", ) @@ -532,19 +520,15 @@ mod tests { fn test_resolve_dependency_evil_redirect() { let (sources, location) = setup(); let cache = HttpCache::new(&location); - let evil_specifier = - ModuleSpecifier::resolve_url("https://deno.land/x/evil.ts").unwrap(); + let evil_specifier = resolve_url("https://deno.land/x/evil.ts").unwrap(); let mut evil_headers = HashMap::new(); evil_headers .insert("location".to_string(), "file:///etc/passwd".to_string()); - cache - .set(evil_specifier.as_url(), evil_headers, b"") - .unwrap(); - let remote_specifier = - ModuleSpecifier::resolve_url("https://deno.land/x/mod.ts").unwrap(); + cache.set(&evil_specifier, evil_headers, b"").unwrap(); + let remote_specifier = resolve_url("https://deno.land/x/mod.ts").unwrap(); cache .set( - remote_specifier.as_url(), + &remote_specifier, Default::default(), b"export * from \"./evil.ts\";", ) @@ -556,8 +540,8 @@ mod tests { #[test] fn test_sources_resolve_specifier_non_supported_schema() { let (sources, _) = setup(); - let specifier = ModuleSpecifier::resolve_url("foo://a/b/c.ts") - .expect("could not create specifier"); + let specifier = + resolve_url("foo://a/b/c.ts").expect("could not create specifier"); let sources = sources.0.lock().unwrap(); let mut redirects = sources.redirects.clone(); let http_cache = sources.http_cache.clone(); diff --git a/cli/lsp/tsc.rs b/cli/lsp/tsc.rs index 5dc7200b74..9a8dc7b217 100644 --- a/cli/lsp/tsc.rs +++ b/cli/lsp/tsc.rs @@ -19,6 +19,7 @@ use deno_core::error::anyhow; use deno_core::error::custom_error; use deno_core::error::AnyError; use deno_core::json_op_sync; +use deno_core::resolve_url; use deno_core::serde::Deserialize; use deno_core::serde::Serialize; use deno_core::serde_json; @@ -113,7 +114,7 @@ impl Default for Assets { .iter() .map(|(k, v)| { let url_str = format!("asset:///{}", k); - let specifier = ModuleSpecifier::resolve_url(&url_str).unwrap(); + let specifier = resolve_url(&url_str).unwrap(); let asset = AssetDocument::new(v); (specifier, Some(asset)) }) @@ -478,8 +479,7 @@ impl DocumentSpan { line_index: &LineIndex, language_server: &mut language_server::Inner, ) -> Option { - let target_specifier = - ModuleSpecifier::resolve_url(&self.file_name).unwrap(); + let target_specifier = resolve_url(&self.file_name).unwrap(); if let Ok(target_line_index) = language_server.get_line_index(target_specifier).await { @@ -615,8 +615,7 @@ impl RenameLocations { HashMap::new(); for location in self.locations.iter() { let uri = utils::normalize_file_name(&location.document_span.file_name)?; - let specifier = - ModuleSpecifier::resolve_url(&location.document_span.file_name)?; + let specifier = resolve_url(&location.document_span.file_name)?; // ensure TextDocumentEdit for `location.file_name`. if text_document_edit_map.get(&uri).is_none() { @@ -778,7 +777,7 @@ impl FileTextChanges { &self, language_server: &mut language_server::Inner, ) -> Result { - let specifier = ModuleSpecifier::resolve_url(&self.file_name)?; + let specifier = resolve_url(&self.file_name)?; let line_index = language_server.get_line_index(specifier.clone()).await?; let edits = self .text_changes @@ -787,7 +786,7 @@ impl FileTextChanges { .collect(); Ok(lsp::TextDocumentEdit { text_document: lsp::OptionalVersionedTextDocumentIdentifier { - uri: specifier.as_url().clone(), + uri: specifier.clone(), version: language_server.document_version(specifier), }, edits, @@ -1063,7 +1062,7 @@ fn cache_snapshot( .snapshots .contains_key(&(specifier.clone().into(), version.clone().into())) { - let s = ModuleSpecifier::resolve_url(&specifier)?; + let s = resolve_url(&specifier)?; let content = state.state_snapshot.documents.content(&s)?.unwrap(); state .snapshots @@ -1156,7 +1155,7 @@ fn get_change_range(state: &mut State, args: Value) -> Result { fn get_length(state: &mut State, args: Value) -> Result { let mark = state.state_snapshot.performance.mark("op_get_length"); let v: SourceSnapshotArgs = serde_json::from_value(args)?; - let specifier = ModuleSpecifier::resolve_url(&v.specifier)?; + let specifier = resolve_url(&v.specifier)?; if let Some(Some(asset)) = state.state_snapshot.assets.get(&specifier) { Ok(json!(asset.length)) } else if state.state_snapshot.documents.contains_key(&specifier) { @@ -1186,7 +1185,7 @@ struct GetTextArgs { fn get_text(state: &mut State, args: Value) -> Result { let mark = state.state_snapshot.performance.mark("op_get_text"); let v: GetTextArgs = serde_json::from_value(args)?; - let specifier = ModuleSpecifier::resolve_url(&v.specifier)?; + let specifier = resolve_url(&v.specifier)?; let content = if let Some(Some(content)) = state.state_snapshot.assets.get(&specifier) { content.text.clone() @@ -1208,7 +1207,7 @@ fn resolve(state: &mut State, args: Value) -> Result { let mark = state.state_snapshot.performance.mark("op_resolve"); let v: ResolveArgs = serde_json::from_value(args)?; let mut resolved = Vec::>::new(); - let referrer = ModuleSpecifier::resolve_url(&v.base)?; + let referrer = resolve_url(&v.base)?; let sources = &mut state.state_snapshot.sources; if state.state_snapshot.documents.contains_key(&referrer) { @@ -1311,8 +1310,8 @@ struct ScriptVersionArgs { fn script_version(state: &mut State, args: Value) -> Result { let mark = state.state_snapshot.performance.mark("op_script_version"); let v: ScriptVersionArgs = serde_json::from_value(args)?; - let specifier = ModuleSpecifier::resolve_url(&v.specifier)?; - if specifier.as_url().scheme() == "asset" { + let specifier = resolve_url(&v.specifier)?; + if specifier.scheme() == "asset" { return if state.state_snapshot.assets.contains_key(&specifier) { Ok(json!("1")) } else { @@ -1673,8 +1672,8 @@ mod tests { fn mock_state_snapshot(sources: Vec<(&str, &str, i32)>) -> StateSnapshot { let mut documents = DocumentCache::default(); for (specifier, content, version) in sources { - let specifier = ModuleSpecifier::resolve_url(specifier) - .expect("failed to create specifier"); + let specifier = + resolve_url(specifier).expect("failed to create specifier"); documents.open(specifier, version, content); } StateSnapshot { @@ -1769,8 +1768,7 @@ mod tests { }), vec![("file:///a.ts", r#"console.log("hello deno");"#, 1)], ); - let specifier = ModuleSpecifier::resolve_url("file:///a.ts") - .expect("could not resolve url"); + let specifier = resolve_url("file:///a.ts").expect("could not resolve url"); let result = request( &mut runtime, state_snapshot, @@ -1815,8 +1813,7 @@ mod tests { }), vec![("file:///a.ts", r#"console.log(document.location);"#, 1)], ); - let specifier = ModuleSpecifier::resolve_url("file:///a.ts") - .expect("could not resolve url"); + let specifier = resolve_url("file:///a.ts").expect("could not resolve url"); let result = request( &mut runtime, state_snapshot, @@ -1849,8 +1846,7 @@ mod tests { 1, )], ); - let specifier = ModuleSpecifier::resolve_url("file:///a.ts") - .expect("could not resolve url"); + let specifier = resolve_url("file:///a.ts").expect("could not resolve url"); let result = request( &mut runtime, state_snapshot, @@ -1879,8 +1875,7 @@ mod tests { 1, )], ); - let specifier = ModuleSpecifier::resolve_url("file:///a.ts") - .expect("could not resolve url"); + let specifier = resolve_url("file:///a.ts").expect("could not resolve url"); let result = request( &mut runtime, state_snapshot, @@ -1933,8 +1928,7 @@ mod tests { 1, )], ); - let specifier = ModuleSpecifier::resolve_url("file:///a.ts") - .expect("could not resolve url"); + let specifier = resolve_url("file:///a.ts").expect("could not resolve url"); let result = request( &mut runtime, state_snapshot, @@ -1970,8 +1964,7 @@ mod tests { 1, )], ); - let specifier = ModuleSpecifier::resolve_url("file:///a.ts") - .expect("could not resolve url"); + let specifier = resolve_url("file:///a.ts").expect("could not resolve url"); let result = request( &mut runtime, state_snapshot, @@ -2028,8 +2021,7 @@ mod tests { }), vec![("file:///a.ts", r#"const url = new URL("b.js", import."#, 1)], ); - let specifier = ModuleSpecifier::resolve_url("file:///a.ts") - .expect("could not resolve url"); + let specifier = resolve_url("file:///a.ts").expect("could not resolve url"); let result = request( &mut runtime, state_snapshot, @@ -2052,8 +2044,8 @@ mod tests { }), vec![], ); - let specifier = ModuleSpecifier::resolve_url("asset:///lib.esnext.d.ts") - .expect("could not resolve url"); + let specifier = + resolve_url("asset:///lib.esnext.d.ts").expect("could not resolve url"); let result = request( &mut runtime, state_snapshot, diff --git a/cli/lsp/utils.rs b/cli/lsp/utils.rs index 8f4de9c05e..c7eae31473 100644 --- a/cli/lsp/utils.rs +++ b/cli/lsp/utils.rs @@ -1,6 +1,7 @@ // Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. use deno_core::error::AnyError; +use deno_core::resolve_url; use deno_core::url::Position; use deno_core::url::Url; use deno_core::ModuleSpecifier; @@ -19,12 +20,11 @@ pub fn normalize_file_name(file_name: &str) -> Result { pub fn normalize_specifier( specifier: &ModuleSpecifier, ) -> Result { - let url = specifier.as_url(); - if url.scheme() == "file" { - Ok(url.clone()) + if specifier.scheme() == "file" { + Ok(specifier.clone()) } else { let specifier_str = - format!("deno:///{}", url.as_str().replacen("://", "/", 1)); + format!("deno:///{}", specifier.as_str().replacen("://", "/", 1)); Url::parse(&specifier_str).map_err(|err| err.into()) } } @@ -41,12 +41,12 @@ pub fn normalize_url(url: Url) -> ModuleSpecifier { if let Ok(specifier) = percent_encoding::percent_decode_str(&specifier_str).decode_utf8() { - if let Ok(specifier) = ModuleSpecifier::resolve_url(&specifier) { + if let Ok(specifier) = resolve_url(&specifier) { return specifier; } } } - ModuleSpecifier::from(url) + url } #[cfg(test)] @@ -63,8 +63,7 @@ mod tests { #[test] fn test_normalize_specifier() { - let fixture = - ModuleSpecifier::resolve_url("https://deno.land/x/mod.ts").unwrap(); + let fixture = resolve_url("https://deno.land/x/mod.ts").unwrap(); let actual = normalize_specifier(&fixture).unwrap(); let expected = Url::parse("deno:///https/deno.land/x/mod.ts").unwrap(); assert_eq!(actual, expected); @@ -74,9 +73,6 @@ mod tests { fn test_normalize_url() { let fixture = Url::parse("deno:///https/deno.land/x/mod.ts").unwrap(); let actual = normalize_url(fixture); - assert_eq!( - actual, - ModuleSpecifier::resolve_url("https://deno.land/x/mod.ts").unwrap() - ); + assert_eq!(actual, resolve_url("https://deno.land/x/mod.ts").unwrap()); } } diff --git a/cli/main.rs b/cli/main.rs index bae9fdb94e..779c45d53d 100644 --- a/cli/main.rs +++ b/cli/main.rs @@ -61,6 +61,7 @@ use deno_core::error::generic_error; use deno_core::error::AnyError; use deno_core::futures::future::FutureExt; use deno_core::futures::Future; +use deno_core::resolve_url_or_path; use deno_core::serde_json; use deno_core::serde_json::json; use deno_core::v8_set_flags; @@ -312,12 +313,12 @@ async fn compile_command( let run_flags = tools::standalone::compile_to_runtime_flags(flags.clone(), args)?; - let module_specifier = ModuleSpecifier::resolve_url_or_path(&source_file)?; + let module_specifier = resolve_url_or_path(&source_file)?; let program_state = ProgramState::build(flags.clone()).await?; let deno_dir = &program_state.dir; let output = output.or_else(|| { - infer_name_from_url(module_specifier.as_url()).map(PathBuf::from) + infer_name_from_url(&module_specifier).map(PathBuf::from) }).ok_or_else(|| generic_error( "An executable name was not provided. One could not be inferred from the URL. Aborting.", ))?; @@ -369,7 +370,7 @@ async fn info_command( } let program_state = ProgramState::build(flags).await?; if let Some(specifier) = maybe_specifier { - let specifier = ModuleSpecifier::resolve_url_or_path(&specifier)?; + let specifier = resolve_url_or_path(&specifier)?; let handler = Arc::new(Mutex::new(specifier_handler::FetchHandler::new( &program_state, // info accesses dynamically imported modules just for their information @@ -410,7 +411,7 @@ async fn install_command( preload_flags.inspect_brk = None; let permissions = Permissions::from_options(&preload_flags.clone().into()); let program_state = ProgramState::build(preload_flags).await?; - let main_module = ModuleSpecifier::resolve_url_or_path(&module_url)?; + let main_module = resolve_url_or_path(&module_url)?; let mut worker = create_main_worker(&program_state, main_module.clone(), permissions); // First, fetch and compile the module; this step ensures that the module exists. @@ -453,7 +454,7 @@ async fn cache_command( let program_state = ProgramState::build(flags).await?; for file in files { - let specifier = ModuleSpecifier::resolve_url_or_path(&file)?; + let specifier = resolve_url_or_path(&file)?; program_state .prepare_module_load( specifier, @@ -475,13 +476,11 @@ async fn eval_command( print: bool, ) -> Result<(), AnyError> { // Force TypeScript compile. - let main_module = - ModuleSpecifier::resolve_url_or_path("./$deno$eval.ts").unwrap(); + let main_module = resolve_url_or_path("./$deno$eval.ts").unwrap(); let permissions = Permissions::from_options(&flags.clone().into()); let program_state = ProgramState::build(flags).await?; let mut worker = create_main_worker(&program_state, main_module.clone(), permissions); - let main_module_url = main_module.as_url().to_owned(); // Create a dummy source file. let source_code = if print { format!("console.log({})", code) @@ -491,7 +490,7 @@ async fn eval_command( .into_bytes(); let file = File { - local: main_module_url.to_file_path().unwrap(), + local: main_module.clone().to_file_path().unwrap(), maybe_types: None, media_type: if as_typescript { MediaType::TypeScript @@ -499,7 +498,7 @@ async fn eval_command( MediaType::JavaScript }, source: String::from_utf8(source_code)?, - specifier: ModuleSpecifier::from(main_module_url), + specifier: main_module.clone(), }; // Save our fake file into file fetcher cache @@ -592,8 +591,7 @@ async fn bundle_command( let source_file1 = source_file.clone(); let source_file2 = source_file.clone(); async move { - let module_specifier = - ModuleSpecifier::resolve_url_or_path(&source_file1)?; + let module_specifier = resolve_url_or_path(&source_file1)?; debug!(">>>>> bundle START"); let program_state = ProgramState::build(flags.clone()).await?; @@ -614,7 +612,7 @@ async fn bundle_command( let mut paths_to_watch: Vec = module_graph .get_modules() .iter() - .filter_map(|specifier| specifier.as_url().to_file_path().ok()) + .filter_map(|specifier| specifier.to_file_path().ok()) .collect(); if let Some(import_map) = program_state.flags.import_map_path.as_ref() { @@ -705,7 +703,7 @@ impl DocFileLoader for DocLoader { let resolved_specifier = if let Some(resolved) = maybe_resolved { resolved } else { - ModuleSpecifier::resolve_import(specifier, referrer) + deno_core::resolve_import(specifier, referrer) .map_err(|e| doc::DocError::Resolve(e.to_string()))? }; @@ -717,8 +715,8 @@ impl DocFileLoader for DocLoader { specifier: &str, ) -> Pin>>> { let fetcher = self.fetcher.clone(); - let specifier = ModuleSpecifier::resolve_url_or_path(specifier) - .expect("Expected valid specifier"); + let specifier = + resolve_url_or_path(specifier).expect("Expected valid specifier"); async move { let source_file = fetcher .fetch(&specifier, &Permissions::allow_all()) @@ -762,8 +760,7 @@ async fn doc_command( let path = PathBuf::from(&source_file); let media_type = MediaType::from(&path); let syntax = ast::get_syntax(&media_type); - let module_specifier = - ModuleSpecifier::resolve_url_or_path(&source_file).unwrap(); + let module_specifier = resolve_url_or_path(&source_file).unwrap(); doc_parser .parse_with_reexports(&module_specifier.to_string(), syntax) .await @@ -819,8 +816,7 @@ async fn format_command( } async fn run_repl(flags: Flags) -> Result<(), AnyError> { - let main_module = - ModuleSpecifier::resolve_url_or_path("./$deno$repl.ts").unwrap(); + let main_module = resolve_url_or_path("./$deno$repl.ts").unwrap(); let permissions = Permissions::from_options(&flags.clone().into()); let program_state = ProgramState::build(flags).await?; let mut worker = @@ -833,8 +829,7 @@ async fn run_repl(flags: Flags) -> Result<(), AnyError> { async fn run_from_stdin(flags: Flags) -> Result<(), AnyError> { let program_state = ProgramState::build(flags.clone()).await?; let permissions = Permissions::from_options(&flags.clone().into()); - let main_module = - ModuleSpecifier::resolve_url_or_path("./$deno$stdin.ts").unwrap(); + let main_module = resolve_url_or_path("./$deno$stdin.ts").unwrap(); let mut worker = create_main_worker( &program_state.clone(), main_module.clone(), @@ -843,10 +838,9 @@ async fn run_from_stdin(flags: Flags) -> Result<(), AnyError> { let mut source = Vec::new(); std::io::stdin().read_to_end(&mut source)?; - let main_module_url = main_module.as_url().to_owned(); // Create a dummy source file. let source_file = File { - local: main_module_url.to_file_path().unwrap(), + local: main_module.clone().to_file_path().unwrap(), maybe_types: None, media_type: MediaType::TypeScript, source: String::from_utf8(source)?, @@ -870,7 +864,7 @@ async fn run_with_watch(flags: Flags, script: String) -> Result<(), AnyError> { let script2 = script.clone(); let flags = flags.clone(); async move { - let main_module = ModuleSpecifier::resolve_url_or_path(&script1)?; + let main_module = resolve_url_or_path(&script1)?; let program_state = ProgramState::build(flags).await?; let handler = Arc::new(Mutex::new(FetchHandler::new( &program_state, @@ -888,7 +882,7 @@ async fn run_with_watch(flags: Flags, script: String) -> Result<(), AnyError> { let mut paths_to_watch: Vec = module_graph .get_modules() .iter() - .filter_map(|specifier| specifier.as_url().to_file_path().ok()) + .filter_map(|specifier| specifier.to_file_path().ok()) .collect(); if let Some(import_map) = program_state.flags.import_map_path.as_ref() { @@ -947,7 +941,7 @@ async fn run_command(flags: Flags, script: String) -> Result<(), AnyError> { return run_with_watch(flags, script).await; } - let main_module = ModuleSpecifier::resolve_url_or_path(&script)?; + let main_module = resolve_url_or_path(&script)?; let program_state = ProgramState::build(flags.clone()).await?; let permissions = Permissions::from_options(&flags.clone().into()); let mut worker = @@ -1003,10 +997,10 @@ async fn test_command( } return Ok(()); } - let main_module = ModuleSpecifier::resolve_path("$deno$test.ts")?; + let main_module = deno_core::resolve_path("$deno$test.ts")?; // Create a dummy source file. let source_file = File { - local: main_module.as_url().to_file_path().unwrap(), + local: main_module.to_file_path().unwrap(), maybe_types: None, media_type: MediaType::TypeScript, source: tools::test_runner::render_test_file( @@ -1074,8 +1068,7 @@ async fn test_command( // For now, we'll only report for the command that passed --coverage as a flag. if flags.coverage_dir.is_some() { let mut exclude = test_modules.clone(); - let main_module_url = main_module.as_url().to_owned(); - exclude.push(main_module_url); + exclude.push(main_module.clone()); tools::coverage::report_coverages( program_state.clone(), &coverage_collector.dir, diff --git a/cli/media_type.rs b/cli/media_type.rs index 8117147cda..db64b3922c 100644 --- a/cli/media_type.rs +++ b/cli/media_type.rs @@ -63,15 +63,14 @@ impl<'a> From<&'a String> for MediaType { impl<'a> From<&'a ModuleSpecifier> for MediaType { fn from(specifier: &'a ModuleSpecifier) -> Self { - let url = specifier.as_url(); - let path = if url.scheme() == "file" { - if let Ok(path) = url.to_file_path() { + let path = if specifier.scheme() == "file" { + if let Ok(path) = specifier.to_file_path() { path } else { - PathBuf::from(url.path()) + PathBuf::from(specifier.path()) } } else { - PathBuf::from(url.path()) + PathBuf::from(specifier.path()) }; MediaType::from_path(&path) } @@ -245,7 +244,7 @@ mod tests { ]; for (specifier, expected) in fixtures { - let actual = ModuleSpecifier::resolve_url_or_path(specifier).unwrap(); + let actual = deno_core::resolve_url_or_path(specifier).unwrap(); assert_eq!(MediaType::from(&actual), expected); } } diff --git a/cli/module_graph.rs b/cli/module_graph.rs index ca068fb811..dc4495157d 100644 --- a/cli/module_graph.rs +++ b/cli/module_graph.rs @@ -33,6 +33,7 @@ use deno_core::error::get_custom_error_class; use deno_core::error::Context; use deno_core::futures::stream::FuturesUnordered; use deno_core::futures::stream::StreamExt; +use deno_core::resolve_url_or_path; use deno_core::serde::Deserialize; use deno_core::serde::Deserializer; use deno_core::serde::Serialize; @@ -150,7 +151,7 @@ impl swc_bundler::Load for BundleLoader<'_> { ) -> Result { match file { swc_common::FileName::Custom(filename) => { - let specifier = ModuleSpecifier::resolve_url_or_path(filename) + let specifier = resolve_url_or_path(filename) .context("Failed to convert swc FileName to ModuleSpecifier.")?; if let Some(src) = self.graph.get_source(&specifier) { let media_type = self @@ -259,7 +260,7 @@ impl Default for Module { maybe_types: None, maybe_version: None, media_type: MediaType::Unknown, - specifier: ModuleSpecifier::resolve_url("file:///example.js").unwrap(), + specifier: deno_core::resolve_url("file:///example.js").unwrap(), source: "".to_string(), source_path: PathBuf::new(), } @@ -449,11 +450,11 @@ impl Module { remapped_import = true; module_specifier } else { - ModuleSpecifier::resolve_import(specifier, self.specifier.as_str())? + deno_core::resolve_import(specifier, self.specifier.as_str())? }; - let referrer_scheme = self.specifier.as_url().scheme(); - let specifier_scheme = specifier.as_url().scheme(); + let referrer_scheme = self.specifier.scheme(); + let specifier_scheme = specifier.scheme(); let location = maybe_location.unwrap_or(Location { filename: self.specifier.to_string(), line: 0, @@ -1769,7 +1770,7 @@ impl swc_bundler::Resolve for Graph { specifier: &str, ) -> Result { let referrer = if let swc_common::FileName::Custom(referrer) = referrer { - ModuleSpecifier::resolve_url_or_path(referrer) + resolve_url_or_path(referrer) .context("Cannot resolve swc FileName to a module specifier")? } else { unreachable!( @@ -1995,7 +1996,7 @@ pub mod tests { let media_type = MediaType::from(&source_path); let source = fs::read_to_string(&source_path) .map_err(|err| (specifier.clone(), err.into()))?; - let is_remote = specifier.as_url().scheme() != "file"; + let is_remote = specifier.scheme() != "file"; Ok(CachedModule { source, @@ -2200,7 +2201,7 @@ pub mod tests { let fixtures = c.join("tests/bundle"); for (specifier, expected_str) in tests { - let specifier = ModuleSpecifier::resolve_url_or_path(specifier).unwrap(); + let specifier = resolve_url_or_path(specifier).unwrap(); let handler = Arc::new(Mutex::new(MockSpecifierHandler { fixtures: fixtures.clone(), ..MockSpecifierHandler::default() @@ -2224,9 +2225,8 @@ pub mod tests { #[tokio::test] async fn test_graph_check_emit() { - let specifier = - ModuleSpecifier::resolve_url_or_path("file:///tests/main.ts") - .expect("could not resolve module"); + let specifier = resolve_url_or_path("file:///tests/main.ts") + .expect("could not resolve module"); let (graph, handler) = setup(specifier).await; let result_info = graph .check(CheckOptions { @@ -2247,9 +2247,8 @@ pub mod tests { #[tokio::test] async fn test_graph_check_ignores_dynamic_import_errors() { - let specifier = - ModuleSpecifier::resolve_url_or_path("file:///tests/dynamicimport.ts") - .expect("could not resolve module"); + let specifier = resolve_url_or_path("file:///tests/dynamicimport.ts") + .expect("could not resolve module"); let (graph, _) = setup(specifier).await; let result_info = graph .check(CheckOptions { @@ -2265,9 +2264,8 @@ pub mod tests { #[tokio::test] async fn fix_graph_check_emit_diagnostics() { - let specifier = - ModuleSpecifier::resolve_url_or_path("file:///tests/diag.ts") - .expect("could not resolve module"); + let specifier = resolve_url_or_path("file:///tests/diag.ts") + .expect("could not resolve module"); let (graph, handler) = setup(specifier).await; let result_info = graph .check(CheckOptions { @@ -2290,9 +2288,8 @@ pub mod tests { #[tokio::test] async fn test_graph_check_no_emit() { - let specifier = - ModuleSpecifier::resolve_url_or_path("file:///tests/main.ts") - .expect("could not resolve module"); + let specifier = resolve_url_or_path("file:///tests/main.ts") + .expect("could not resolve module"); let (graph, handler) = setup(specifier).await; let result_info = graph .check(CheckOptions { @@ -2313,7 +2310,7 @@ pub mod tests { #[tokio::test] async fn fix_graph_check_mjs_root() { - let specifier = ModuleSpecifier::resolve_url_or_path("file:///tests/a.mjs") + let specifier = resolve_url_or_path("file:///tests/a.mjs") .expect("could not resolve module"); let (graph, handler) = setup(specifier).await; let result_info = graph @@ -2334,7 +2331,7 @@ pub mod tests { #[tokio::test] async fn fix_graph_check_types_root() { - let specifier = ModuleSpecifier::resolve_url_or_path("file:///typesref.js") + let specifier = resolve_url_or_path("file:///typesref.js") .expect("could not resolve module"); let (graph, _) = setup(specifier).await; let result_info = graph @@ -2351,9 +2348,8 @@ pub mod tests { #[tokio::test] async fn test_graph_check_user_config() { - let specifier = - ModuleSpecifier::resolve_url_or_path("file:///tests/checkwithconfig.ts") - .expect("could not resolve module"); + let specifier = resolve_url_or_path("file:///tests/checkwithconfig.ts") + .expect("could not resolve module"); let (graph, handler) = setup(specifier.clone()).await; let result_info = graph .check(CheckOptions { @@ -2397,8 +2393,7 @@ pub mod tests { #[tokio::test] async fn test_graph_emit() { - let specifier = - ModuleSpecifier::resolve_url_or_path("file:///a.ts").unwrap(); + let specifier = resolve_url_or_path("file:///a.ts").unwrap(); let graph = setup_memory( specifier, map!( @@ -2438,8 +2433,7 @@ pub mod tests { #[tokio::test] async fn test_graph_emit_bundle() { - let specifier = - ModuleSpecifier::resolve_url_or_path("file:///a.ts").unwrap(); + let specifier = resolve_url_or_path("file:///a.ts").unwrap(); let graph = setup_memory( specifier, map!( @@ -2474,8 +2468,7 @@ pub mod tests { #[tokio::test] async fn fix_graph_emit_declaration() { - let specifier = - ModuleSpecifier::resolve_url_or_path("file:///a.ts").unwrap(); + let specifier = resolve_url_or_path("file:///a.ts").unwrap(); let graph = setup_memory( specifier, map!( @@ -2519,9 +2512,8 @@ pub mod tests { #[tokio::test] async fn test_graph_info() { - let specifier = - ModuleSpecifier::resolve_url_or_path("file:///tests/main.ts") - .expect("could not resolve module"); + let specifier = resolve_url_or_path("file:///tests/main.ts") + .expect("could not resolve module"); let (graph, _) = setup(specifier).await; let info = graph.info().expect("could not get info"); assert!(info.compiled.is_none()); @@ -2532,16 +2524,15 @@ pub mod tests { assert!(info.map.is_none()); assert_eq!( info.module, - ModuleSpecifier::resolve_url_or_path("file:///tests/main.ts").unwrap() + resolve_url_or_path("file:///tests/main.ts").unwrap() ); assert_eq!(info.total_size, 344); } #[tokio::test] async fn test_graph_import_json() { - let specifier = - ModuleSpecifier::resolve_url_or_path("file:///tests/importjson.ts") - .expect("could not resolve module"); + let specifier = resolve_url_or_path("file:///tests/importjson.ts") + .expect("could not resolve module"); let c = PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").unwrap()); let fixtures = c.join("tests/module_graph"); let handler = Arc::new(Mutex::new(MockSpecifierHandler { @@ -2564,9 +2555,8 @@ pub mod tests { // to be actually emitted. // // This also exercises "@deno-types" and type references. - let specifier = - ModuleSpecifier::resolve_url_or_path("file:///tests/main.ts") - .expect("could not resolve module"); + let specifier = resolve_url_or_path("file:///tests/main.ts") + .expect("could not resolve module"); let (mut graph, handler) = setup(specifier).await; let result_info = graph.transpile(TranspileOptions::default()).unwrap(); assert_eq!(result_info.stats.0.len(), 3); @@ -2592,19 +2582,17 @@ pub mod tests { assert_eq!(h.deps_calls.len(), 7); assert_eq!( h.deps_calls[0].0, - ModuleSpecifier::resolve_url_or_path("file:///tests/main.ts").unwrap() + resolve_url_or_path("file:///tests/main.ts").unwrap() ); assert_eq!(h.deps_calls[0].1.len(), 1); assert_eq!( h.deps_calls[1].0, - ModuleSpecifier::resolve_url_or_path("https://deno.land/x/lib/mod.js") - .unwrap() + resolve_url_or_path("https://deno.land/x/lib/mod.js").unwrap() ); assert_eq!(h.deps_calls[1].1.len(), 3); assert_eq!( h.deps_calls[2].0, - ModuleSpecifier::resolve_url_or_path("https://deno.land/x/lib/mod.d.ts") - .unwrap() + resolve_url_or_path("https://deno.land/x/lib/mod.d.ts").unwrap() ); assert_eq!(h.deps_calls[2].1.len(), 3, "should have 3 dependencies"); // sometimes the calls are not deterministic, and so checking the contents @@ -2617,9 +2605,8 @@ pub mod tests { #[tokio::test] async fn test_graph_transpile_user_config() { - let specifier = - ModuleSpecifier::resolve_url_or_path("https://deno.land/x/transpile.tsx") - .expect("could not resolve module"); + let specifier = resolve_url_or_path("https://deno.land/x/transpile.tsx") + .expect("could not resolve module"); let (mut graph, handler) = setup(specifier).await; let result_info = graph .transpile(TranspileOptions { @@ -2667,9 +2654,8 @@ pub mod tests { ..Default::default() })); let mut builder = GraphBuilder::new(handler, maybe_import_map, None); - let specifier = - ModuleSpecifier::resolve_url_or_path("file:///tests/importremap.ts") - .expect("could not resolve module"); + let specifier = resolve_url_or_path("file:///tests/importremap.ts") + .expect("could not resolve module"); builder.add(&specifier, false).await.expect("could not add"); builder.get_graph(); } @@ -2687,9 +2673,8 @@ pub mod tests { ..MockSpecifierHandler::default() })); let mut builder = GraphBuilder::new(handler.clone(), None, maybe_lockfile); - let specifier = - ModuleSpecifier::resolve_url_or_path("file:///tests/main.ts") - .expect("could not resolve module"); + let specifier = resolve_url_or_path("file:///tests/main.ts") + .expect("could not resolve module"); builder .add(&specifier, false) .await diff --git a/cli/module_loader.rs b/cli/module_loader.rs index 7b15bc77d4..26b9e28fff 100644 --- a/cli/module_loader.rs +++ b/cli/module_loader.rs @@ -76,7 +76,7 @@ impl ModuleLoader for CliModuleLoader { ) -> Result { // FIXME(bartlomieju): hacky way to provide compatibility with repl let referrer = if referrer.is_empty() && self.program_state.flags.repl { - "" + deno_core::DUMMY_SPECIFIER } else { referrer }; @@ -90,8 +90,7 @@ impl ModuleLoader for CliModuleLoader { } } - let module_specifier = - ModuleSpecifier::resolve_import(specifier, referrer)?; + let module_specifier = deno_core::resolve_import(specifier, referrer)?; Ok(module_specifier) } diff --git a/cli/ops/runtime_compiler.rs b/cli/ops/runtime_compiler.rs index f54e7d2061..79f378d6db 100644 --- a/cli/ops/runtime_compiler.rs +++ b/cli/ops/runtime_compiler.rs @@ -13,11 +13,11 @@ use deno_core::error::generic_error; use deno_core::error::type_error; use deno_core::error::AnyError; use deno_core::error::Context; +use deno_core::resolve_url_or_path; use deno_core::serde_json; use deno_core::serde_json::json; use deno_core::serde_json::Value; use deno_core::BufVec; -use deno_core::ModuleSpecifier; use deno_core::OpState; use deno_runtime::permissions::Permissions; use serde::Deserialize; @@ -79,12 +79,10 @@ async fn op_emit( )?)) }; let maybe_import_map = if let Some(import_map_str) = args.import_map_path { - let import_map_specifier = - ModuleSpecifier::resolve_url_or_path(&import_map_str) - .context(format!("Bad URL (\"{}\") for import map.", import_map_str))?; - let import_map_url = import_map_specifier.as_url(); + let import_map_specifier = resolve_url_or_path(&import_map_str) + .context(format!("Bad URL (\"{}\") for import map.", import_map_str))?; let import_map = if let Some(value) = args.import_map { - ImportMap::from_json(&import_map_url.to_string(), &value.to_string())? + ImportMap::from_json(import_map_specifier.as_str(), &value.to_string())? } else { let file = program_state .file_fetcher @@ -99,7 +97,7 @@ async fn op_emit( None }; let mut builder = GraphBuilder::new(handler, maybe_import_map, None); - let root_specifier = ModuleSpecifier::resolve_url_or_path(&root_specifier)?; + let root_specifier = resolve_url_or_path(&root_specifier)?; builder .add(&root_specifier, is_dynamic) .await diff --git a/cli/program_state.rs b/cli/program_state.rs index 7009813687..f05c740d55 100644 --- a/cli/program_state.rs +++ b/cli/program_state.rs @@ -21,6 +21,7 @@ use deno_core::error::anyhow; use deno_core::error::get_custom_error_class; use deno_core::error::AnyError; use deno_core::error::Context; +use deno_core::resolve_url; use deno_core::url::Url; use deno_core::ModuleSource; use deno_core::ModuleSpecifier; @@ -99,7 +100,7 @@ impl ProgramState { exit_unstable("--import-map") } let import_map_specifier = - ModuleSpecifier::resolve_url_or_path(&import_map_url).context( + deno_core::resolve_url_or_path(&import_map_url).context( format!("Bad URL (\"{}\") for import map.", import_map_url), )?; let file = file_fetcher @@ -291,8 +292,8 @@ impl ProgramState { // else, like a refactored file_fetcher. impl SourceMapGetter for ProgramState { fn get_source_map(&self, file_name: &str) -> Option> { - if let Ok(specifier) = ModuleSpecifier::resolve_url(file_name) { - if let Some((code, maybe_map)) = self.get_emit(&specifier.as_url()) { + if let Ok(specifier) = resolve_url(file_name) { + if let Some((code, maybe_map)) = self.get_emit(&specifier) { let code = String::from_utf8(code).unwrap(); source_map_from_code(code).or(maybe_map) } else if let Ok(source) = self.load(specifier, None) { @@ -310,7 +311,7 @@ impl SourceMapGetter for ProgramState { file_name: &str, line_number: usize, ) -> Option { - if let Ok(specifier) = ModuleSpecifier::resolve_url(file_name) { + if let Ok(specifier) = resolve_url(file_name) { self.file_fetcher.get_source(&specifier).map(|out| { // Do NOT use .lines(): it skips the terminating empty line. // (due to internally using .split_terminator() instead of .split()) diff --git a/cli/source_maps.rs b/cli/source_maps.rs index 4820d8c80d..aa96b31be4 100644 --- a/cli/source_maps.rs +++ b/cli/source_maps.rs @@ -3,7 +3,6 @@ //! This mod provides functions to remap a `JsError` based on a source map. use deno_core::error::JsError; -use deno_core::ModuleSpecifier; use sourcemap::SourceMap; use std::collections::HashMap; use std::str; @@ -129,8 +128,7 @@ pub fn get_orig_position( // sometimes only the basename of the URL, or has unwanted `<`/`>` // around it. Use the `file_name` we get from V8 if // `source_file_name` does not parse as a URL. - let file_name = match ModuleSpecifier::resolve_url(source_file_name) - { + let file_name = match deno_core::resolve_url(source_file_name) { Ok(m) => m.to_string(), Err(_) => file_name, }; diff --git a/cli/specifier_handler.rs b/cli/specifier_handler.rs index e780b94d04..0be41b929b 100644 --- a/cli/specifier_handler.rs +++ b/cli/specifier_handler.rs @@ -70,7 +70,7 @@ pub struct CachedModule { impl Default for CachedModule { fn default() -> Self { - let specifier = ModuleSpecifier::resolve_url("file:///example.js").unwrap(); + let specifier = deno_core::resolve_url("file:///example.js").unwrap(); CachedModule { is_remote: false, maybe_dependencies: None, @@ -304,7 +304,7 @@ impl SpecifierHandler for FetchHandler { (requested_specifier.clone(), err) } })?; - let url = source_file.specifier.as_url(); + let url = &source_file.specifier; let is_remote = !(url.scheme() == "file" || url.scheme() == "data"); let filename = disk_cache.get_cache_filename_with_extension(url, "meta"); let maybe_version = if let Some(filename) = filename { @@ -371,7 +371,7 @@ impl SpecifierHandler for FetchHandler { ) -> Result, AnyError> { let filename = self .disk_cache - .get_cache_filename_with_extension(specifier.as_url(), "buildinfo"); + .get_cache_filename_with_extension(specifier, "buildinfo"); if let Some(filename) = filename { if let Ok(tsbuildinfo) = self.disk_cache.get(&filename) { Ok(Some(String::from_utf8(tsbuildinfo)?)) @@ -390,7 +390,7 @@ impl SpecifierHandler for FetchHandler { ) -> Result<(), AnyError> { let filename = self .disk_cache - .get_cache_filename_with_extension(specifier.as_url(), "buildinfo") + .get_cache_filename_with_extension(specifier, "buildinfo") .unwrap(); debug!("set_tsbuildinfo - filename {:?}", filename); self @@ -406,17 +406,16 @@ impl SpecifierHandler for FetchHandler { ) -> Result<(), AnyError> { match emit { Emit::Cli((code, maybe_map)) => { - let url = specifier.as_url(); let filename = self .disk_cache - .get_cache_filename_with_extension(url, "js") + .get_cache_filename_with_extension(specifier, "js") .unwrap(); self.disk_cache.set(&filename, code.as_bytes())?; if let Some(map) = maybe_map { let filename = self .disk_cache - .get_cache_filename_with_extension(url, "js.map") + .get_cache_filename_with_extension(specifier, "js.map") .unwrap(); self.disk_cache.set(&filename, map.as_bytes())?; } @@ -452,7 +451,7 @@ impl SpecifierHandler for FetchHandler { let compiled_file_metadata = CompiledFileMetadata { version_hash }; let filename = self .disk_cache - .get_cache_filename_with_extension(specifier.as_url(), "meta") + .get_cache_filename_with_extension(specifier, "meta") .unwrap(); self @@ -492,7 +491,7 @@ impl SpecifierHandler for MemoryHandler { } let result = if let Some(source) = self.sources.get(&specifier_text) { let media_type = MediaType::from(&specifier); - let is_remote = specifier.as_url().scheme() != "file"; + let is_remote = specifier.scheme() != "file"; Ok(CachedModule { source: source.to_string(), @@ -568,6 +567,7 @@ pub mod tests { use super::*; use crate::file_fetcher::CacheSetting; use crate::http_cache::HttpCache; + use deno_core::resolve_url_or_path; use tempfile::TempDir; macro_rules! map ( @@ -609,10 +609,9 @@ pub mod tests { async fn test_fetch_handler_fetch() { let _http_server_guard = test_util::http_server(); let (_, mut file_fetcher) = setup(); - let specifier = ModuleSpecifier::resolve_url_or_path( - "http://localhost:4545/cli/tests/subdir/mod2.ts", - ) - .unwrap(); + let specifier = + resolve_url_or_path("http://localhost:4545/cli/tests/subdir/mod2.ts") + .unwrap(); let cached_module: CachedModule = file_fetcher .fetch(specifier.clone(), None, false) .await @@ -631,10 +630,9 @@ pub mod tests { async fn test_fetch_handler_set_cache() { let _http_server_guard = test_util::http_server(); let (_, mut file_fetcher) = setup(); - let specifier = ModuleSpecifier::resolve_url_or_path( - "http://localhost:4545/cli/tests/subdir/mod2.ts", - ) - .unwrap(); + let specifier = + resolve_url_or_path("http://localhost:4545/cli/tests/subdir/mod2.ts") + .unwrap(); let cached_module: CachedModule = file_fetcher .fetch(specifier.clone(), None, false) .await @@ -658,15 +656,14 @@ pub mod tests { async fn test_fetch_handler_is_remote() { let _http_server_guard = test_util::http_server(); let (_, mut file_fetcher) = setup(); - let specifier = ModuleSpecifier::resolve_url_or_path( - "http://localhost:4545/cli/tests/subdir/mod2.ts", - ) - .unwrap(); + let specifier = + resolve_url_or_path("http://localhost:4545/cli/tests/subdir/mod2.ts") + .unwrap(); let cached_module: CachedModule = file_fetcher.fetch(specifier, None, false).await.unwrap(); assert_eq!(cached_module.is_remote, true); let c = PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").unwrap()); - let specifier = ModuleSpecifier::resolve_url_or_path( + let specifier = resolve_url_or_path( c.join("tests/subdir/mod1.ts").as_os_str().to_str().unwrap(), ) .unwrap(); @@ -701,8 +698,7 @@ pub mod tests { .map(|(k, v)| (k.to_string(), v.to_string())) .collect(); let mut handler = MemoryHandler::new(sources); - let specifier = - ModuleSpecifier::resolve_url_or_path("file:///a.ts").unwrap(); + let specifier = resolve_url_or_path("file:///a.ts").unwrap(); let actual: CachedModule = handler .fetch(specifier.clone(), None, false) .await @@ -713,8 +709,7 @@ pub mod tests { assert_eq!(actual.media_type, MediaType::TypeScript); assert_eq!(actual.is_remote, false); - let specifier = - ModuleSpecifier::resolve_url_or_path("file:///b.ts").unwrap(); + let specifier = resolve_url_or_path("file:///b.ts").unwrap(); let actual: CachedModule = handler .fetch(specifier.clone(), None, false) .await @@ -725,8 +720,7 @@ pub mod tests { assert_eq!(actual.media_type, MediaType::TypeScript); assert_eq!(actual.is_remote, false); - let specifier = - ModuleSpecifier::resolve_url_or_path("https://deno.land/x/c.js").unwrap(); + let specifier = resolve_url_or_path("https://deno.land/x/c.js").unwrap(); let actual: CachedModule = handler .fetch(specifier.clone(), None, false) .await @@ -737,9 +731,7 @@ pub mod tests { assert_eq!(actual.media_type, MediaType::JavaScript); assert_eq!(actual.is_remote, true); - let specifier = - ModuleSpecifier::resolve_url_or_path("https://deno.land/x/d.d.ts") - .unwrap(); + let specifier = resolve_url_or_path("https://deno.land/x/d.d.ts").unwrap(); let actual: CachedModule = handler .fetch(specifier.clone(), None, false) .await @@ -751,14 +743,13 @@ pub mod tests { assert_eq!(actual.is_remote, true); let specifier = - ModuleSpecifier::resolve_url_or_path("https://deno.land/x/missing.ts") - .unwrap(); + resolve_url_or_path("https://deno.land/x/missing.ts").unwrap(); handler .fetch(specifier.clone(), None, false) .await .expect_err("should have errored"); - let specifier = ModuleSpecifier::resolve_url_or_path("/a.ts").unwrap(); + let specifier = resolve_url_or_path("/a.ts").unwrap(); let actual: CachedModule = handler .fetch(specifier.clone(), None, false) .await @@ -769,8 +760,7 @@ pub mod tests { assert_eq!(actual.media_type, MediaType::TypeScript); assert_eq!(actual.is_remote, false); - let specifier = - ModuleSpecifier::resolve_url_or_path("file:///C:/a.ts").unwrap(); + let specifier = resolve_url_or_path("file:///C:/a.ts").unwrap(); let actual: CachedModule = handler .fetch(specifier.clone(), None, false) .await diff --git a/cli/standalone.rs b/cli/standalone.rs index 8e596e5a62..e3fb6fe432 100644 --- a/cli/standalone.rs +++ b/cli/standalone.rs @@ -6,6 +6,7 @@ use deno_core::error::type_error; use deno_core::error::AnyError; use deno_core::error::Context; use deno_core::futures::FutureExt; +use deno_core::resolve_url; use deno_core::serde::Deserialize; use deno_core::serde::Serialize; use deno_core::serde_json; @@ -123,7 +124,7 @@ impl ModuleLoader for EmbeddedModuleLoader { "Self-contained binaries don't support module loading", )); } - Ok(ModuleSpecifier::resolve_url(specifier)?) + Ok(resolve_url(specifier)?) } fn load( @@ -155,7 +156,7 @@ pub async fn run( source_code: String, metadata: Metadata, ) -> Result<(), AnyError> { - let main_module = ModuleSpecifier::resolve_url(SPECIFIER)?; + let main_module = resolve_url(SPECIFIER)?; let permissions = Permissions::from_options(&metadata.permissions); let module_loader = Rc::new(EmbeddedModuleLoader(source_code)); let create_web_worker_cb = Arc::new(|_| { diff --git a/cli/tools/coverage.rs b/cli/tools/coverage.rs index b06f4d2bec..f4edd18d44 100644 --- a/cli/tools/coverage.rs +++ b/cli/tools/coverage.rs @@ -11,7 +11,6 @@ use deno_core::error::AnyError; use deno_core::serde_json; use deno_core::serde_json::json; use deno_core::url::Url; -use deno_core::ModuleSpecifier; use deno_runtime::inspector::InspectorSession; use deno_runtime::permissions::Permissions; use serde::Deserialize; @@ -389,7 +388,7 @@ pub async fn report_coverages( let mut coverage_reporter = PrettyCoverageReporter::new(quiet); for script_coverage in coverages { let module_specifier = - ModuleSpecifier::resolve_url_or_path(&script_coverage.url)?; + deno_core::resolve_url_or_path(&script_coverage.url)?; program_state .prepare_module_load( module_specifier.clone(), diff --git a/cli/tsc.rs b/cli/tsc.rs index a8a33ca116..535447c543 100644 --- a/cli/tsc.rs +++ b/cli/tsc.rs @@ -11,6 +11,7 @@ use deno_core::error::bail; use deno_core::error::AnyError; use deno_core::error::Context; use deno_core::json_op_sync; +use deno_core::resolve_url_or_path; use deno_core::serde_json; use deno_core::serde_json::json; use deno_core::serde_json::Value; @@ -96,11 +97,11 @@ fn hash_data_url( media_type: &MediaType, ) -> String { assert_eq!( - specifier.as_url().scheme(), + specifier.scheme(), "data", "Specifier must be a data: specifier." ); - let hash = crate::checksum::gen(&[specifier.as_url().path().as_bytes()]); + let hash = crate::checksum::gen(&[specifier.path().as_bytes()]); format!("data:///{}{}", hash, media_type.as_ts_extension()) } @@ -108,15 +109,14 @@ fn hash_data_url( /// and so we have to detect the apparent media type based on extensions it /// supports. fn get_tsc_media_type(specifier: &ModuleSpecifier) -> MediaType { - let url = specifier.as_url(); - let path = if url.scheme() == "file" { - if let Ok(path) = url.to_file_path() { + let path = if specifier.scheme() == "file" { + if let Ok(path) = specifier.to_file_path() { path } else { - PathBuf::from(url.path()) + PathBuf::from(specifier.path()) } } else { - PathBuf::from(url.path()) + PathBuf::from(specifier.path()) }; match path.extension() { None => MediaType::Unknown, @@ -262,7 +262,7 @@ fn emit(state: &mut State, args: Value) -> Result { } else if let Some(remapped_specifier) = state.root_map.get(s) { remapped_specifier.clone() } else { - ModuleSpecifier::resolve_url_or_path(s).unwrap() + resolve_url_or_path(s).unwrap() } }) .collect(); @@ -286,7 +286,7 @@ struct LoadArgs { fn load(state: &mut State, args: Value) -> Result { let v: LoadArgs = serde_json::from_value(args) .context("Invalid request from JavaScript for \"op_load\".")?; - let specifier = ModuleSpecifier::resolve_url_or_path(&v.specifier) + let specifier = resolve_url_or_path(&v.specifier) .context("Error converting a string module specifier for \"op_load\".")?; let mut hash: Option = None; let mut media_type = MediaType::Unknown; @@ -349,7 +349,7 @@ fn resolve(state: &mut State, args: Value) -> Result { } else if let Some(remapped_base) = state.root_map.get(&v.base) { remapped_base.clone() } else { - ModuleSpecifier::resolve_url_or_path(&v.base).context( + resolve_url_or_path(&v.base).context( "Error converting a string module specifier for \"op_resolve\".", )? }; @@ -373,8 +373,7 @@ fn resolve(state: &mut State, args: Value) -> Result { resolved_specifier ) }; - let resolved_specifier_str = if resolved_specifier.as_url().scheme() - == "data" + let resolved_specifier_str = if resolved_specifier.scheme() == "data" { let specifier_str = hash_data_url(&resolved_specifier, &media_type); state @@ -433,7 +432,7 @@ pub fn exec(request: Request) -> Result { .root_names .iter() .map(|(s, mt)| { - if s.as_url().scheme() == "data" { + if s.scheme() == "data" { let specifier_str = hash_data_url(s, mt); data_url_map.insert(specifier_str.clone(), s.clone()); specifier_str @@ -520,9 +519,8 @@ mod tests { maybe_hash_data: Option>>, maybe_tsbuildinfo: Option, ) -> State { - let specifier = maybe_specifier.unwrap_or_else(|| { - ModuleSpecifier::resolve_url_or_path("file:///main.ts").unwrap() - }); + let specifier = maybe_specifier + .unwrap_or_else(|| resolve_url_or_path("file:///main.ts").unwrap()); let hash_data = maybe_hash_data.unwrap_or_else(|| vec![b"".to_vec()]); let c = PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").unwrap()); let fixtures = c.join("tests/tsc2"); @@ -619,7 +617,7 @@ mod tests { #[test] fn test_hash_data_url() { - let specifier = ModuleSpecifier::resolve_url( + let specifier = deno_core::resolve_url( "data:application/javascript,console.log(\"Hello%20Deno\");", ) .unwrap(); @@ -642,7 +640,7 @@ mod tests { ("file:///.tsbuildinfo", MediaType::Unknown), ]; for (specifier, media_type) in fixtures { - let specifier = ModuleSpecifier::resolve_url_or_path(specifier).unwrap(); + let specifier = resolve_url_or_path(specifier).unwrap(); assert_eq!(get_tsc_media_type(&specifier), media_type); } } @@ -666,7 +664,7 @@ mod tests { state.emitted_files[0], EmittedFile { data: "some file content".to_string(), - maybe_specifiers: Some(vec![ModuleSpecifier::resolve_url_or_path( + maybe_specifiers: Some(vec![resolve_url_or_path( "file:///some/file.ts" ) .unwrap()]), @@ -697,10 +695,7 @@ mod tests { #[tokio::test] async fn test_load() { let mut state = setup( - Some( - ModuleSpecifier::resolve_url_or_path("https://deno.land/x/mod.ts") - .unwrap(), - ), + Some(resolve_url_or_path("https://deno.land/x/mod.ts").unwrap()), None, Some("some content".to_string()), ) @@ -731,10 +726,7 @@ mod tests { #[tokio::test] async fn test_load_asset() { let mut state = setup( - Some( - ModuleSpecifier::resolve_url_or_path("https://deno.land/x/mod.ts") - .unwrap(), - ), + Some(resolve_url_or_path("https://deno.land/x/mod.ts").unwrap()), None, Some("some content".to_string()), ) @@ -753,10 +745,7 @@ mod tests { #[tokio::test] async fn test_load_tsbuildinfo() { let mut state = setup( - Some( - ModuleSpecifier::resolve_url_or_path("https://deno.land/x/mod.ts") - .unwrap(), - ), + Some(resolve_url_or_path("https://deno.land/x/mod.ts").unwrap()), None, Some("some content".to_string()), ) @@ -795,10 +784,7 @@ mod tests { #[tokio::test] async fn test_resolve() { let mut state = setup( - Some( - ModuleSpecifier::resolve_url_or_path("https://deno.land/x/a.ts") - .unwrap(), - ), + Some(resolve_url_or_path("https://deno.land/x/a.ts").unwrap()), None, None, ) @@ -814,10 +800,7 @@ mod tests { #[tokio::test] async fn test_resolve_empty() { let mut state = setup( - Some( - ModuleSpecifier::resolve_url_or_path("https://deno.land/x/a.ts") - .unwrap(), - ), + Some(resolve_url_or_path("https://deno.land/x/a.ts").unwrap()), None, None, ) @@ -874,8 +857,7 @@ mod tests { #[tokio::test] async fn test_exec_basic() { - let specifier = - ModuleSpecifier::resolve_url_or_path("https://deno.land/x/a.ts").unwrap(); + let specifier = resolve_url_or_path("https://deno.land/x/a.ts").unwrap(); let actual = test_exec(&specifier) .await .expect("exec should not have errored"); @@ -887,8 +869,7 @@ mod tests { #[tokio::test] async fn test_exec_reexport_dts() { - let specifier = - ModuleSpecifier::resolve_url_or_path("file:///reexports.ts").unwrap(); + let specifier = resolve_url_or_path("file:///reexports.ts").unwrap(); let actual = test_exec(&specifier) .await .expect("exec should not have errored"); @@ -900,8 +881,7 @@ mod tests { #[tokio::test] async fn fix_lib_ref() { - let specifier = - ModuleSpecifier::resolve_url_or_path("file:///libref.ts").unwrap(); + let specifier = resolve_url_or_path("file:///libref.ts").unwrap(); let actual = test_exec(&specifier) .await .expect("exec should not have errored"); diff --git a/core/bindings.rs b/core/bindings.rs index 08f2441663..1f6b85174b 100644 --- a/core/bindings.rs +++ b/core/bindings.rs @@ -454,9 +454,11 @@ fn eval_context( } */ let tc_scope = &mut v8::TryCatch::new(scope); - let name = - v8::String::new(tc_scope, url.as_ref().map_or("", Url::as_str)) - .unwrap(); + let name = v8::String::new( + tc_scope, + url.as_ref().map_or(crate::DUMMY_SPECIFIER, Url::as_str), + ) + .unwrap(); let origin = script_origin(tc_scope, name); let maybe_script = v8::Script::compile(tc_scope, source, Some(&origin)); diff --git a/core/lib.rs b/core/lib.rs index 9f4fec003f..deea9d2812 100644 --- a/core/lib.rs +++ b/core/lib.rs @@ -42,8 +42,13 @@ pub use crate::async_cell::AsyncRefFuture; pub use crate::async_cell::RcLike; pub use crate::async_cell::RcRef; pub use crate::flags::v8_set_flags; +pub use crate::module_specifier::resolve_import; +pub use crate::module_specifier::resolve_path; +pub use crate::module_specifier::resolve_url; +pub use crate::module_specifier::resolve_url_or_path; pub use crate::module_specifier::ModuleResolutionError; pub use crate::module_specifier::ModuleSpecifier; +pub use crate::module_specifier::DUMMY_SPECIFIER; pub use crate::modules::FsModuleLoader; pub use crate::modules::ModuleId; pub use crate::modules::ModuleLoadId; diff --git a/core/module_specifier.rs b/core/module_specifier.rs index a9ce570998..dc6b4d6bf6 100644 --- a/core/module_specifier.rs +++ b/core/module_specifier.rs @@ -1,9 +1,6 @@ // Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. use crate::normalize_path; -use serde::de; -use serde::Deserialize; -use serde::Deserializer; use std::env::current_dir; use std::error::Error; use std::fmt; @@ -11,6 +8,8 @@ use std::path::PathBuf; use url::ParseError; use url::Url; +pub const DUMMY_SPECIFIER: &str = ""; + /// Error indicating the reason resolving a module specifier failed. #[derive(Clone, Debug, Eq, PartialEq)] pub enum ModuleResolutionError { @@ -51,174 +50,126 @@ impl fmt::Display for ModuleResolutionError { } } -#[derive( - Debug, Clone, Eq, Hash, PartialEq, serde::Serialize, Ord, PartialOrd, -)] /// Resolved module specifier -pub struct ModuleSpecifier(Url); +pub type ModuleSpecifier = Url; -impl ModuleSpecifier { - fn is_dummy_specifier(specifier: &str) -> bool { - specifier == "" - } +/// Resolves module using this algorithm: +/// https://html.spec.whatwg.org/multipage/webappapis.html#resolve-a-module-specifier +pub fn resolve_import( + specifier: &str, + base: &str, +) -> Result { + let url = match Url::parse(specifier) { + // 1. Apply the URL parser to specifier. + // If the result is not failure, return he result. + Ok(url) => url, - pub fn as_url(&self) -> &Url { - &self.0 - } - - pub fn as_str(&self) -> &str { - self.0.as_str() - } - - /// Resolves module using this algorithm: - /// https://html.spec.whatwg.org/multipage/webappapis.html#resolve-a-module-specifier - pub fn resolve_import( - specifier: &str, - base: &str, - ) -> Result { - let url = match Url::parse(specifier) { - // 1. Apply the URL parser to specifier. - // If the result is not failure, return he result. - Ok(url) => url, - - // 2. If specifier does not start with the character U+002F SOLIDUS (/), - // the two-character sequence U+002E FULL STOP, U+002F SOLIDUS (./), - // or the three-character sequence U+002E FULL STOP, U+002E FULL STOP, - // U+002F SOLIDUS (../), return failure. - Err(ParseError::RelativeUrlWithoutBase) - if !(specifier.starts_with('/') - || specifier.starts_with("./") - || specifier.starts_with("../")) => - { - let maybe_referrer = if base.is_empty() { - None - } else { - Some(base.to_string()) - }; - return Err(ImportPrefixMissing(specifier.to_string(), maybe_referrer)); - } - - // 3. Return the result of applying the URL parser to specifier with base - // URL as the base URL. - Err(ParseError::RelativeUrlWithoutBase) => { - let base = if ModuleSpecifier::is_dummy_specifier(base) { - // Handle case, happening under e.g. repl. - // Use CWD for such case. - - // Forcefully join base to current dir. - // Otherwise, later joining in Url would be interpreted in - // the parent directory (appending trailing slash does not work) - let path = current_dir().unwrap().join(base); - Url::from_file_path(path).unwrap() - } else { - Url::parse(base).map_err(InvalidBaseUrl)? - }; - base.join(&specifier).map_err(InvalidUrl)? - } - - // If parsing the specifier as a URL failed for a different reason than - // it being relative, always return the original error. We don't want to - // return `ImportPrefixMissing` or `InvalidBaseUrl` if the real - // problem lies somewhere else. - Err(err) => return Err(InvalidUrl(err)), - }; - - Ok(ModuleSpecifier(url)) - } - - /// Converts a string representing an absolute URL into a ModuleSpecifier. - pub fn resolve_url( - url_str: &str, - ) -> Result { - Url::parse(url_str) - .map(ModuleSpecifier) - .map_err(ModuleResolutionError::InvalidUrl) - } - - /// Takes a string representing either an absolute URL or a file path, - /// as it may be passed to deno as a command line argument. - /// The string is interpreted as a URL if it starts with a valid URI scheme, - /// e.g. 'http:' or 'file:' or 'git+ssh:'. If not, it's interpreted as a - /// file path; if it is a relative path it's resolved relative to the current - /// working directory. - pub fn resolve_url_or_path( - specifier: &str, - ) -> Result { - if Self::specifier_has_uri_scheme(specifier) { - Self::resolve_url(specifier) - } else { - Self::resolve_path(specifier) + // 2. If specifier does not start with the character U+002F SOLIDUS (/), + // the two-character sequence U+002E FULL STOP, U+002F SOLIDUS (./), + // or the three-character sequence U+002E FULL STOP, U+002E FULL STOP, + // U+002F SOLIDUS (../), return failure. + Err(ParseError::RelativeUrlWithoutBase) + if !(specifier.starts_with('/') + || specifier.starts_with("./") + || specifier.starts_with("../")) => + { + let maybe_referrer = if base.is_empty() { + None + } else { + Some(base.to_string()) + }; + return Err(ImportPrefixMissing(specifier.to_string(), maybe_referrer)); } - } - /// Converts a string representing a relative or absolute path into a - /// ModuleSpecifier. A relative path is considered relative to the current - /// working directory. - pub fn resolve_path( - path_str: &str, - ) -> Result { - let path = current_dir().unwrap().join(path_str); - let path = normalize_path(&path); - Url::from_file_path(path.clone()) - .map(ModuleSpecifier) - .map_err(|()| ModuleResolutionError::InvalidPath(path)) - } + // 3. Return the result of applying the URL parser to specifier with base + // URL as the base URL. + Err(ParseError::RelativeUrlWithoutBase) => { + let base = if base == DUMMY_SPECIFIER { + // Handle case, happening under e.g. repl. + // Use CWD for such case. - /// Returns true if the input string starts with a sequence of characters - /// that could be a valid URI scheme, like 'https:', 'git+ssh:' or 'data:'. - /// - /// According to RFC 3986 (https://tools.ietf.org/html/rfc3986#section-3.1), - /// a valid scheme has the following format: - /// scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) - /// - /// We additionally require the scheme to be at least 2 characters long, - /// because otherwise a windows path like c:/foo would be treated as a URL, - /// while no schemes with a one-letter name actually exist. - fn specifier_has_uri_scheme(specifier: &str) -> bool { - let mut chars = specifier.chars(); - let mut len = 0usize; - // THe first character must be a letter. + // Forcefully join base to current dir. + // Otherwise, later joining in Url would be interpreted in + // the parent directory (appending trailing slash does not work) + let path = current_dir().unwrap().join(base); + Url::from_file_path(path).unwrap() + } else { + Url::parse(base).map_err(InvalidBaseUrl)? + }; + base.join(&specifier).map_err(InvalidUrl)? + } + + // If parsing the specifier as a URL failed for a different reason than + // it being relative, always return the original error. We don't want to + // return `ImportPrefixMissing` or `InvalidBaseUrl` if the real + // problem lies somewhere else. + Err(err) => return Err(InvalidUrl(err)), + }; + + Ok(url) +} + +/// Converts a string representing an absolute URL into a ModuleSpecifier. +pub fn resolve_url( + url_str: &str, +) -> Result { + Url::parse(url_str).map_err(ModuleResolutionError::InvalidUrl) +} + +/// Takes a string representing either an absolute URL or a file path, +/// as it may be passed to deno as a command line argument. +/// The string is interpreted as a URL if it starts with a valid URI scheme, +/// e.g. 'http:' or 'file:' or 'git+ssh:'. If not, it's interpreted as a +/// file path; if it is a relative path it's resolved relative to the current +/// working directory. +pub fn resolve_url_or_path( + specifier: &str, +) -> Result { + if specifier_has_uri_scheme(specifier) { + resolve_url(specifier) + } else { + resolve_path(specifier) + } +} + +/// Converts a string representing a relative or absolute path into a +/// ModuleSpecifier. A relative path is considered relative to the current +/// working directory. +pub fn resolve_path( + path_str: &str, +) -> Result { + let path = current_dir().unwrap().join(path_str); + let path = normalize_path(&path); + Url::from_file_path(path.clone()) + .map_err(|()| ModuleResolutionError::InvalidPath(path)) +} + +/// Returns true if the input string starts with a sequence of characters +/// that could be a valid URI scheme, like 'https:', 'git+ssh:' or 'data:'. +/// +/// According to RFC 3986 (https://tools.ietf.org/html/rfc3986#section-3.1), +/// a valid scheme has the following format: +/// scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) +/// +/// We additionally require the scheme to be at least 2 characters long, +/// because otherwise a windows path like c:/foo would be treated as a URL, +/// while no schemes with a one-letter name actually exist. +fn specifier_has_uri_scheme(specifier: &str) -> bool { + let mut chars = specifier.chars(); + let mut len = 0usize; + // THe first character must be a letter. + match chars.next() { + Some(c) if c.is_ascii_alphabetic() => len += 1, + _ => return false, + } + // Second and following characters must be either a letter, number, + // plus sign, minus sign, or dot. + loop { match chars.next() { - Some(c) if c.is_ascii_alphabetic() => len += 1, + Some(c) if c.is_ascii_alphanumeric() || "+-.".contains(c) => len += 1, + Some(':') if len >= 2 => return true, _ => return false, } - // Second and following characters must be either a letter, number, - // plus sign, minus sign, or dot. - loop { - match chars.next() { - Some(c) if c.is_ascii_alphanumeric() || "+-.".contains(c) => len += 1, - Some(':') if len >= 2 => return true, - _ => return false, - } - } - } -} - -impl fmt::Display for ModuleSpecifier { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.0.fmt(f) - } -} - -impl From for ModuleSpecifier { - fn from(url: Url) -> Self { - ModuleSpecifier(url) - } -} - -impl PartialEq for ModuleSpecifier { - fn eq(&self, other: &String) -> bool { - &self.to_string() == other - } -} - -impl<'de> Deserialize<'de> for ModuleSpecifier { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let url_str: String = Deserialize::deserialize(deserializer)?; - ModuleSpecifier::resolve_url(&url_str).map_err(de::Error::custom) } } @@ -306,9 +257,7 @@ mod tests { ]; for (specifier, base, expected_url) in tests { - let url = ModuleSpecifier::resolve_import(specifier, base) - .unwrap() - .to_string(); + let url = resolve_import(specifier, base).unwrap().to_string(); assert_eq!(url, expected_url); } } @@ -385,7 +334,7 @@ mod tests { ]; for (specifier, base, expected_err) in tests { - let err = ModuleSpecifier::resolve_import(specifier, base).unwrap_err(); + let err = resolve_import(specifier, base).unwrap_err(); assert_eq!(err, expected_err); } } @@ -487,9 +436,7 @@ mod tests { } for (specifier, expected_url) in tests { - let url = ModuleSpecifier::resolve_url_or_path(specifier) - .unwrap() - .to_string(); + let url = resolve_url_or_path(specifier).unwrap().to_string(); assert_eq!(url, expected_url); } } @@ -509,7 +456,7 @@ mod tests { } for (specifier, expected_err) in tests { - let err = ModuleSpecifier::resolve_url_or_path(specifier).unwrap_err(); + let err = resolve_url_or_path(specifier).unwrap_err(); assert_eq!(err, expected_err); } } @@ -539,7 +486,7 @@ mod tests { ]; for (specifier, expected) in tests { - let result = ModuleSpecifier::specifier_has_uri_scheme(specifier); + let result = specifier_has_uri_scheme(specifier); assert_eq!(result, expected); } } @@ -565,8 +512,7 @@ mod tests { fn test_deserialize_module_specifier() { let actual: ModuleSpecifier = from_value(json!("http://deno.land/x/mod.ts")).unwrap(); - let expected = - ModuleSpecifier::resolve_url("http://deno.land/x/mod.ts").unwrap(); + let expected = resolve_url("http://deno.land/x/mod.ts").unwrap(); assert_eq!(actual, expected); } } diff --git a/core/modules.rs b/core/modules.rs index 815db2fb9e..aefb3e491d 100644 --- a/core/modules.rs +++ b/core/modules.rs @@ -145,7 +145,7 @@ impl ModuleLoader for FsModuleLoader { referrer: &str, _is_main: bool, ) -> Result { - Ok(ModuleSpecifier::resolve_import(specifier, referrer)?) + Ok(crate::resolve_import(specifier, referrer)?) } fn load( @@ -157,7 +157,7 @@ impl ModuleLoader for FsModuleLoader { ) -> Pin> { let module_specifier = module_specifier.clone(); async move { - let path = module_specifier.as_url().to_file_path().map_err(|_| { + let path = module_specifier.to_file_path().map_err(|_| { generic_error(format!( "Provided module specifier \"{}\" is not a file URL.", module_specifier @@ -649,11 +649,10 @@ mod tests { eprintln!(">> RESOLVING, S: {}, R: {}", specifier, referrer); - let output_specifier = - match ModuleSpecifier::resolve_import(specifier, referrer) { - Ok(specifier) => specifier, - Err(..) => return Err(MockError::ResolveErr.into()), - }; + let output_specifier = match crate::resolve_import(specifier, referrer) { + Ok(specifier) => specifier, + Err(..) => return Err(MockError::ResolveErr.into()), + }; if mock_source_code(&output_specifier.to_string()).is_some() { Ok(output_specifier) @@ -715,7 +714,7 @@ mod tests { module_loader: Some(loader), ..Default::default() }); - let spec = ModuleSpecifier::resolve_url("file:///a.js").unwrap(); + let spec = crate::resolve_url("file:///a.js").unwrap(); let a_id_fut = runtime.load_module(&spec, None); let a_id = futures::executor::block_on(a_id_fut).expect("Failed to load"); @@ -741,17 +740,17 @@ mod tests { assert_eq!( modules.get_children(a_id), Some(&vec![ - ModuleSpecifier::resolve_url("file:///b.js").unwrap(), - ModuleSpecifier::resolve_url("file:///c.js").unwrap() + crate::resolve_url("file:///b.js").unwrap(), + crate::resolve_url("file:///c.js").unwrap() ]) ); assert_eq!( modules.get_children(b_id), - Some(&vec![ModuleSpecifier::resolve_url("file:///c.js").unwrap()]) + Some(&vec![crate::resolve_url("file:///c.js").unwrap()]) ); assert_eq!( modules.get_children(c_id), - Some(&vec![ModuleSpecifier::resolve_url("file:///d.js").unwrap()]) + Some(&vec![crate::resolve_url("file:///d.js").unwrap()]) ); assert_eq!(modules.get_children(d_id), Some(&vec![])); } @@ -782,7 +781,7 @@ mod tests { }); let fut = async move { - let spec = ModuleSpecifier::resolve_url("file:///circular1.js").unwrap(); + let spec = crate::resolve_url("file:///circular1.js").unwrap(); let result = runtime.load_module(&spec, None).await; assert!(result.is_ok()); let circular1_id = result.unwrap(); @@ -807,16 +806,12 @@ mod tests { assert_eq!( modules.get_children(circular1_id), - Some(&vec![ - ModuleSpecifier::resolve_url("file:///circular2.js").unwrap() - ]) + Some(&vec![crate::resolve_url("file:///circular2.js").unwrap()]) ); assert_eq!( modules.get_children(circular2_id), - Some(&vec![ - ModuleSpecifier::resolve_url("file:///circular3.js").unwrap() - ]) + Some(&vec![crate::resolve_url("file:///circular3.js").unwrap()]) ); assert!(modules.get_id("file:///circular3.js").is_some()); @@ -824,8 +819,8 @@ mod tests { assert_eq!( modules.get_children(circular3_id), Some(&vec![ - ModuleSpecifier::resolve_url("file:///circular1.js").unwrap(), - ModuleSpecifier::resolve_url("file:///circular2.js").unwrap() + crate::resolve_url("file:///circular1.js").unwrap(), + crate::resolve_url("file:///circular2.js").unwrap() ]) ); } @@ -858,7 +853,7 @@ mod tests { }); let fut = async move { - let spec = ModuleSpecifier::resolve_url("file:///redirect1.js").unwrap(); + let spec = crate::resolve_url("file:///redirect1.js").unwrap(); let result = runtime.load_module(&spec, None).await; println!(">> result {:?}", result); assert!(result.is_ok()); @@ -923,7 +918,7 @@ mod tests { module_loader: Some(loader), ..Default::default() }); - let spec = ModuleSpecifier::resolve_url("file:///main.js").unwrap(); + let spec = crate::resolve_url("file:///main.js").unwrap(); let mut recursive_load = runtime.load_module(&spec, None).boxed_local(); let result = recursive_load.poll_unpin(&mut cx); @@ -971,7 +966,7 @@ mod tests { module_loader: Some(loader), ..Default::default() }); - let spec = ModuleSpecifier::resolve_url("file:///bad_import.js").unwrap(); + let spec = crate::resolve_url("file:///bad_import.js").unwrap(); let mut load_fut = runtime.load_module(&spec, None).boxed_local(); let result = load_fut.poll_unpin(&mut cx); if let Poll::Ready(Err(err)) = result { @@ -1005,8 +1000,7 @@ mod tests { // In default resolution code should be empty. // Instead we explicitly pass in our own code. // The behavior should be very similar to /a.js. - let spec = - ModuleSpecifier::resolve_url("file:///main_with_code.js").unwrap(); + let spec = crate::resolve_url("file:///main_with_code.js").unwrap(); let main_id_fut = runtime .load_module(&spec, Some(MAIN_WITH_CODE_SRC.to_owned())) .boxed_local(); @@ -1033,17 +1027,17 @@ mod tests { assert_eq!( modules.get_children(main_id), Some(&vec![ - ModuleSpecifier::resolve_url("file:///b.js").unwrap(), - ModuleSpecifier::resolve_url("file:///c.js").unwrap() + crate::resolve_url("file:///b.js").unwrap(), + crate::resolve_url("file:///c.js").unwrap() ]) ); assert_eq!( modules.get_children(b_id), - Some(&vec![ModuleSpecifier::resolve_url("file:///c.js").unwrap()]) + Some(&vec![crate::resolve_url("file:///c.js").unwrap()]) ); assert_eq!( modules.get_children(c_id), - Some(&vec![ModuleSpecifier::resolve_url("file:///d.js").unwrap()]) + Some(&vec![crate::resolve_url("file:///d.js").unwrap()]) ); assert_eq!(modules.get_children(d_id), Some(&vec![])); } diff --git a/core/runtime.rs b/core/runtime.rs index 9db1669cde..31229aaa3b 100644 --- a/core/runtime.rs +++ b/core/runtime.rs @@ -1224,8 +1224,7 @@ impl JsRuntime { let is_main = load.state == LoadState::LoadingRoot && !load.is_dynamic_import(); - let referrer_specifier = - ModuleSpecifier::resolve_url(&module_url_found).unwrap(); + let referrer_specifier = crate::resolve_url(&module_url_found).unwrap(); let state_rc = Self::state(self.v8_isolate()); // #A There are 3 cases to handle at this moment: @@ -2200,7 +2199,7 @@ pub mod tests { self.count.fetch_add(1, Ordering::Relaxed); assert_eq!(specifier, "./b.js"); assert_eq!(referrer, "file:///a.js"); - let s = ModuleSpecifier::resolve_import(specifier, referrer).unwrap(); + let s = crate::resolve_import(specifier, referrer).unwrap(); Ok(s) } @@ -2272,7 +2271,7 @@ pub mod tests { let imports = state.modules.get_children(mod_a); assert_eq!( imports, - Some(&vec![ModuleSpecifier::resolve_url("file:///b.js").unwrap()]) + Some(&vec![crate::resolve_url("file:///b.js").unwrap()]) ); } let mod_b = runtime @@ -2313,7 +2312,7 @@ pub mod tests { self.count.fetch_add(1, Ordering::Relaxed); assert_eq!(specifier, "/foo.js"); assert_eq!(referrer, "file:///dyn_import2.js"); - let s = ModuleSpecifier::resolve_import(specifier, referrer).unwrap(); + let s = crate::resolve_import(specifier, referrer).unwrap(); Ok(s) } @@ -2377,7 +2376,7 @@ pub mod tests { assert!(c < 4); assert_eq!(specifier, "./b.js"); assert_eq!(referrer, "file:///dyn_import3.js"); - let s = ModuleSpecifier::resolve_import(specifier, referrer).unwrap(); + let s = crate::resolve_import(specifier, referrer).unwrap(); Ok(s) } @@ -2504,7 +2503,7 @@ pub mod tests { ) -> Result { assert_eq!(specifier, "file:///main.js"); assert_eq!(referrer, "."); - let s = ModuleSpecifier::resolve_import(specifier, referrer).unwrap(); + let s = crate::resolve_import(specifier, referrer).unwrap(); Ok(s) } @@ -2526,7 +2525,7 @@ pub mod tests { ..Default::default() }); - let specifier = ModuleSpecifier::resolve_url("file:///main.js").unwrap(); + let specifier = crate::resolve_url("file:///main.js").unwrap(); let source_code = "Deno.core.print('hello\\n')".to_string(); let module_id = futures::executor::block_on( diff --git a/runtime/examples/hello_runtime.rs b/runtime/examples/hello_runtime.rs index 9b8e17a4c1..e8bf9841ee 100644 --- a/runtime/examples/hello_runtime.rs +++ b/runtime/examples/hello_runtime.rs @@ -2,7 +2,6 @@ use deno_core::error::AnyError; use deno_core::FsModuleLoader; -use deno_core::ModuleSpecifier; use deno_runtime::permissions::Permissions; use deno_runtime::worker::MainWorker; use deno_runtime::worker::WorkerOptions; @@ -44,7 +43,7 @@ async fn main() -> Result<(), AnyError> { let js_path = Path::new(env!("CARGO_MANIFEST_DIR")).join("examples/hello_runtime.js"); - let main_module = ModuleSpecifier::resolve_path(&js_path.to_string_lossy())?; + let main_module = deno_core::resolve_path(&js_path.to_string_lossy())?; let permissions = Permissions::allow_all(); let mut worker = diff --git a/runtime/ops/runtime.rs b/runtime/ops/runtime.rs index 4b81c579f7..77abc45b72 100644 --- a/runtime/ops/runtime.rs +++ b/runtime/ops/runtime.rs @@ -26,8 +26,8 @@ fn op_main_module( _zero_copy: &mut [ZeroCopyBuf], ) -> Result { let main = state.borrow::().to_string(); - let main_url = ModuleSpecifier::resolve_url_or_path(&main)?; - if main_url.as_url().scheme() == "file" { + let main_url = deno_core::resolve_url_or_path(&main)?; + if main_url.scheme() == "file" { let main_path = std::env::current_dir().unwrap().join(main_url.to_string()); state .borrow::() diff --git a/runtime/ops/worker_host.rs b/runtime/ops/worker_host.rs index d68fa02e8a..6b375605fd 100644 --- a/runtime/ops/worker_host.rs +++ b/runtime/ops/worker_host.rs @@ -482,7 +482,7 @@ fn op_create_worker( state.put::(create_module_loader.clone()); state.put::(worker_id + 1); - let module_specifier = ModuleSpecifier::resolve_url(&specifier)?; + let module_specifier = deno_core::resolve_url(&specifier)?; let worker_name = args_name.unwrap_or_else(|| "".to_string()); let (handle_sender, handle_receiver) = diff --git a/runtime/permissions.rs b/runtime/permissions.rs index b568c0a4ff..e89a8747db 100644 --- a/runtime/permissions.rs +++ b/runtime/permissions.rs @@ -580,9 +580,8 @@ impl Permissions { &self, specifier: &ModuleSpecifier, ) -> Result<(), AnyError> { - let url = specifier.as_url(); - match url.scheme() { - "file" => match url.to_file_path() { + match specifier.scheme() { + "file" => match specifier.to_file_path() { Ok(path) => self.check_read(&path), Err(_) => Err(uri_error(format!( "Invalid file path.\n Specifier: {}", @@ -590,7 +589,7 @@ impl Permissions { ))), }, "data" => Ok(()), - _ => self.check_net_url(url), + _ => self.check_net_url(specifier), } } @@ -749,6 +748,7 @@ fn format_host>(host: &(T, Option)) -> String { #[cfg(test)] mod tests { use super::*; + use deno_core::resolve_url_or_path; // Creates vector of strings, Vec macro_rules! svec { @@ -1000,42 +1000,27 @@ mod tests { let mut fixtures = vec![ ( - ModuleSpecifier::resolve_url_or_path("http://localhost:4545/mod.ts") - .unwrap(), + resolve_url_or_path("http://localhost:4545/mod.ts").unwrap(), true, ), ( - ModuleSpecifier::resolve_url_or_path("http://deno.land/x/mod.ts") - .unwrap(), + resolve_url_or_path("http://deno.land/x/mod.ts").unwrap(), false, ), ( - ModuleSpecifier::resolve_url_or_path( - "data:text/plain,Hello%2C%20Deno!", - ) - .unwrap(), + resolve_url_or_path("data:text/plain,Hello%2C%20Deno!").unwrap(), true, ), ]; if cfg!(target_os = "windows") { - fixtures.push(( - ModuleSpecifier::resolve_url_or_path("file:///C:/a/mod.ts").unwrap(), - true, - )); - fixtures.push(( - ModuleSpecifier::resolve_url_or_path("file:///C:/b/mod.ts").unwrap(), - false, - )); + fixtures + .push((resolve_url_or_path("file:///C:/a/mod.ts").unwrap(), true)); + fixtures + .push((resolve_url_or_path("file:///C:/b/mod.ts").unwrap(), false)); } else { - fixtures.push(( - ModuleSpecifier::resolve_url_or_path("file:///a/mod.ts").unwrap(), - true, - )); - fixtures.push(( - ModuleSpecifier::resolve_url_or_path("file:///b/mod.ts").unwrap(), - false, - )); + fixtures.push((resolve_url_or_path("file:///a/mod.ts").unwrap(), true)); + fixtures.push((resolve_url_or_path("file:///b/mod.ts").unwrap(), false)); } for (specifier, expected) in fixtures { @@ -1058,7 +1043,7 @@ mod tests { for url in test_cases { assert!(perms - .check_specifier(&ModuleSpecifier::resolve_url_or_path(url).unwrap()) + .check_specifier(&resolve_url_or_path(url).unwrap()) .is_err()); } } diff --git a/runtime/web_worker.rs b/runtime/web_worker.rs index 0efb547f4d..0847059acc 100644 --- a/runtime/web_worker.rs +++ b/runtime/web_worker.rs @@ -476,8 +476,7 @@ mod tests { use deno_core::serde_json::json; fn create_test_web_worker() -> WebWorker { - let main_module = - ModuleSpecifier::resolve_url_or_path("./hello.js").unwrap(); + let main_module = deno_core::resolve_url_or_path("./hello.js").unwrap(); let module_loader = Rc::new(deno_core::NoopModuleLoader); let create_web_worker_cb = Arc::new(|_| unreachable!()); diff --git a/runtime/worker.rs b/runtime/worker.rs index a619ecc4c7..dca01932db 100644 --- a/runtime/worker.rs +++ b/runtime/worker.rs @@ -260,10 +260,10 @@ impl Drop for MainWorker { #[cfg(test)] mod tests { use super::*; + use deno_core::resolve_url_or_path; fn create_test_worker() -> MainWorker { - let main_module = - ModuleSpecifier::resolve_url_or_path("./hello.js").unwrap(); + let main_module = resolve_url_or_path("./hello.js").unwrap(); let permissions = Permissions::default(); let options = WorkerOptions { @@ -296,8 +296,7 @@ mod tests { .parent() .unwrap() .join("cli/tests/esm_imports_a.js"); - let module_specifier = - ModuleSpecifier::resolve_url_or_path(&p.to_string_lossy()).unwrap(); + let module_specifier = resolve_url_or_path(&p.to_string_lossy()).unwrap(); let mut worker = create_test_worker(); let result = worker.execute_module(&module_specifier).await; if let Err(err) = result { @@ -314,8 +313,7 @@ mod tests { .parent() .unwrap() .join("tests/circular1.js"); - let module_specifier = - ModuleSpecifier::resolve_url_or_path(&p.to_string_lossy()).unwrap(); + let module_specifier = resolve_url_or_path(&p.to_string_lossy()).unwrap(); let mut worker = create_test_worker(); let result = worker.execute_module(&module_specifier).await; if let Err(err) = result { @@ -330,8 +328,7 @@ mod tests { async fn execute_mod_resolve_error() { // "foo" is not a valid module specifier so this should return an error. let mut worker = create_test_worker(); - let module_specifier = - ModuleSpecifier::resolve_url_or_path("does-not-exist").unwrap(); + let module_specifier = resolve_url_or_path("does-not-exist").unwrap(); let result = worker.execute_module(&module_specifier).await; assert!(result.is_err()); } @@ -345,8 +342,7 @@ mod tests { .parent() .unwrap() .join("cli/tests/001_hello.js"); - let module_specifier = - ModuleSpecifier::resolve_url_or_path(&p.to_string_lossy()).unwrap(); + let module_specifier = resolve_url_or_path(&p.to_string_lossy()).unwrap(); let result = worker.execute_module(&module_specifier).await; assert!(result.is_ok()); }