summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cli/disk_cache.rs2
-rw-r--r--cli/file_fetcher.rs44
-rw-r--r--cli/http_cache.rs33
-rw-r--r--cli/lsp/language_server.rs4
-rw-r--r--cli/lsp/sources.rs469
-rw-r--r--cli/lsp/tsc.rs3
6 files changed, 326 insertions, 229 deletions
diff --git a/cli/disk_cache.rs b/cli/disk_cache.rs
index 2b530cae2..2f2cc9e4f 100644
--- a/cli/disk_cache.rs
+++ b/cli/disk_cache.rs
@@ -67,7 +67,7 @@ impl DiskCache {
out.push(path_seg);
}
}
- "http" | "https" | "data" => out = url_to_filename(url),
+ "http" | "https" | "data" => out = url_to_filename(url)?,
"file" => {
let path = match url.to_file_path() {
Ok(path) => path,
diff --git a/cli/file_fetcher.rs b/cli/file_fetcher.rs
index 3e6622408..23ace672c 100644
--- a/cli/file_fetcher.rs
+++ b/cli/file_fetcher.rs
@@ -338,7 +338,12 @@ impl FileFetcher {
bytes: Vec<u8>,
headers: &HashMap<String, String>,
) -> Result<File, AnyError> {
- let local = self.http_cache.get_cache_filename(specifier.as_url());
+ let local = self
+ .http_cache
+ .get_cache_filename(specifier.as_url())
+ .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);
@@ -416,7 +421,12 @@ 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());
+ let local = self
+ .http_cache
+ .get_cache_filename(specifier.as_url())
+ .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
@@ -995,7 +1005,8 @@ mod tests {
let cache_filename = file_fetcher
.http_cache
- .get_cache_filename(specifier.as_url());
+ .get_cache_filename(specifier.as_url())
+ .unwrap();
let mut metadata =
crate::http_cache::Metadata::read(&cache_filename).unwrap();
metadata.headers = HashMap::new();
@@ -1079,7 +1090,8 @@ mod tests {
.unwrap();
let cache_filename = file_fetcher_01
.http_cache
- .get_cache_filename(specifier.as_url());
+ .get_cache_filename(specifier.as_url())
+ .unwrap();
let result = file_fetcher_01
.fetch(&specifier, &Permissions::allow_all())
@@ -1128,14 +1140,16 @@ mod tests {
.unwrap();
let cached_filename = file_fetcher
.http_cache
- .get_cache_filename(specifier.as_url());
+ .get_cache_filename(specifier.as_url())
+ .unwrap();
let redirected_specifier = ModuleSpecifier::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.as_url())
+ .unwrap();
let result = file_fetcher
.fetch(&specifier, &Permissions::allow_all())
@@ -1179,21 +1193,24 @@ mod tests {
.unwrap();
let cached_filename = file_fetcher
.http_cache
- .get_cache_filename(specifier.as_url());
+ .get_cache_filename(specifier.as_url())
+ .unwrap();
let redirected_01_specifier = ModuleSpecifier::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.as_url())
+ .unwrap();
let redirected_02_specifier = ModuleSpecifier::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.as_url())
+ .unwrap();
let result = file_fetcher
.fetch(&specifier, &Permissions::allow_all())
@@ -1265,7 +1282,8 @@ mod tests {
.unwrap();
let redirected_cache_filename = file_fetcher_01
.http_cache
- .get_cache_filename(redirected_specifier.as_url());
+ .get_cache_filename(redirected_specifier.as_url())
+ .unwrap();
let result = file_fetcher_01
.fetch(&specifier, &Permissions::allow_all())
@@ -1340,14 +1358,16 @@ mod tests {
.unwrap();
let cached_filename = file_fetcher
.http_cache
- .get_cache_filename(specifier.as_url());
+ .get_cache_filename(specifier.as_url())
+ .unwrap();
let redirected_specifier = ModuleSpecifier::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.as_url())
+ .unwrap();
let result = file_fetcher
.fetch(&specifier, &Permissions::allow_all())
diff --git a/cli/http_cache.rs b/cli/http_cache.rs
index aa68420b4..e6a1e8ac7 100644
--- a/cli/http_cache.rs
+++ b/cli/http_cache.rs
@@ -6,6 +6,7 @@
/// at hand.
use crate::fs_util;
use crate::http_util::HeadersMap;
+use deno_core::error::generic_error;
use deno_core::error::AnyError;
use deno_core::serde_json;
use deno_core::url::Url;
@@ -23,7 +24,7 @@ pub const CACHE_PERM: u32 = 0o644;
/// This method replaces port part with a special string token (because
/// ":" cannot be used in filename on some platforms).
/// Ex: $DENO_DIR/deps/https/deno.land/
-fn base_url_to_filename(url: &Url) -> PathBuf {
+fn base_url_to_filename(url: &Url) -> Option<PathBuf> {
let mut out = PathBuf::new();
let scheme = url.scheme();
@@ -40,14 +41,12 @@ fn base_url_to_filename(url: &Url) -> PathBuf {
}
"data" => (),
scheme => {
- unimplemented!(
- "Don't know how to create cache name for scheme: {}",
- scheme
- );
+ error!("Don't know how to create cache name for scheme: {}", scheme);
+ return None;
}
};
- out
+ Some(out)
}
/// Turn provided `url` into a hashed filename.
@@ -57,8 +56,8 @@ fn base_url_to_filename(url: &Url) -> PathBuf {
/// strings.
///
/// NOTE: this method is `pub` because it's used in integration_tests
-pub fn url_to_filename(url: &Url) -> PathBuf {
- let mut cache_filename = base_url_to_filename(url);
+pub fn url_to_filename(url: &Url) -> Option<PathBuf> {
+ let mut cache_filename = base_url_to_filename(url)?;
let mut rest_str = url.path().to_string();
if let Some(query) = url.query() {
@@ -70,7 +69,7 @@ pub fn url_to_filename(url: &Url) -> PathBuf {
// in case of static resources doesn't make much sense
let hashed_filename = crate::checksum::gen(&[rest_str.as_bytes()]);
cache_filename.push(hashed_filename);
- cache_filename
+ Some(cache_filename)
}
#[derive(Debug, Clone, Default)]
@@ -133,15 +132,18 @@ impl HttpCache {
})
}
- pub(crate) fn get_cache_filename(&self, url: &Url) -> PathBuf {
- self.location.join(url_to_filename(url))
+ pub(crate) fn get_cache_filename(&self, url: &Url) -> Option<PathBuf> {
+ Some(self.location.join(url_to_filename(url)?))
}
// TODO(bartlomieju): this method should check headers file
// and validate against ETAG/Last-modified-as headers.
// ETAG check is currently done in `cli/file_fetcher.rs`.
pub fn get(&self, url: &Url) -> Result<(File, HeadersMap), AnyError> {
- let cache_filename = self.location.join(url_to_filename(url));
+ let cache_filename = self.location.join(
+ url_to_filename(url)
+ .ok_or_else(|| generic_error("Can't convert url to filename."))?,
+ );
let metadata_filename = Metadata::filename(&cache_filename);
let file = File::open(cache_filename)?;
let metadata = fs::read_to_string(metadata_filename)?;
@@ -155,7 +157,10 @@ impl HttpCache {
headers_map: HeadersMap,
content: &[u8],
) -> Result<(), AnyError> {
- let cache_filename = self.location.join(url_to_filename(url));
+ let cache_filename = self.location.join(
+ url_to_filename(url)
+ .ok_or_else(|| generic_error("Can't convert url to filename."))?,
+ );
// Create parent directory
let parent_filename = cache_filename
.parent()
@@ -266,7 +271,7 @@ mod tests {
for (url, expected) in test_cases.iter() {
let u = Url::parse(url).unwrap();
- let p = url_to_filename(&u);
+ let p = url_to_filename(&u).unwrap();
assert_eq!(p, PathBuf::from(expected));
}
}
diff --git a/cli/lsp/language_server.rs b/cli/lsp/language_server.rs
index 7f7b693f9..6594492a4 100644
--- a/cli/lsp/language_server.rs
+++ b/cli/lsp/language_server.rs
@@ -1946,8 +1946,8 @@ impl Inner {
}
}
_ => {
- if let Some(text) = self.sources.get_text(&specifier) {
- Some(text)
+ if let Some(source) = self.sources.get_source(&specifier) {
+ Some(source)
} else {
error!("The cached sources was not found: {}", specifier);
None
diff --git a/cli/lsp/sources.rs b/cli/lsp/sources.rs
index 62316e4a2..307170d72 100644
--- a/cli/lsp/sources.rs
+++ b/cli/lsp/sources.rs
@@ -40,9 +40,68 @@ pub async fn cache(
builder.add(specifier, false).await
}
+fn get_remote_headers(
+ cache_filename: &Path,
+) -> Option<HashMap<String, String>> {
+ let metadata_path = http_cache::Metadata::filename(cache_filename);
+ let metadata_str = fs::read_to_string(metadata_path).ok()?;
+ let metadata: http_cache::Metadata =
+ serde_json::from_str(&metadata_str).ok()?;
+ Some(metadata.headers)
+}
+
+fn resolve_remote_specifier(
+ specifier: &ModuleSpecifier,
+ http_cache: &HttpCache,
+ redirect_limit: isize,
+) -> Option<ModuleSpecifier> {
+ let cache_filename = http_cache.get_cache_filename(specifier.as_url())?;
+ 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()?;
+ resolve_remote_specifier(&redirect, http_cache, redirect_limit - 1)
+ } else {
+ Some(specifier.clone())
+ }
+ } else {
+ None
+ }
+}
+
+fn resolve_specifier(
+ specifier: &ModuleSpecifier,
+ redirects: &mut HashMap<ModuleSpecifier, ModuleSpecifier>,
+ http_cache: &HttpCache,
+) -> Option<ModuleSpecifier> {
+ let scheme = specifier.as_url().scheme();
+ if !SUPPORTED_SCHEMES.contains(&scheme) {
+ return None;
+ }
+
+ if scheme == "data" {
+ Some(specifier.clone())
+ } else if scheme == "file" {
+ let path = specifier.as_url().to_file_path().ok()?;
+ if path.is_file() {
+ Some(specifier.clone())
+ } else {
+ None
+ }
+ } else if let Some(specifier) = redirects.get(specifier) {
+ Some(specifier.clone())
+ } else {
+ let redirect = resolve_remote_specifier(specifier, http_cache, 10)?;
+ redirects.insert(specifier.clone(), redirect.clone());
+ Some(redirect)
+ }
+}
+
#[derive(Debug, Clone, Default)]
struct Metadata {
dependencies: Option<HashMap<String, analysis::Dependency>>,
+ length_utf16: usize,
line_index: LineIndex,
maybe_types: Option<analysis::ResolvedDependency>,
media_type: MediaType,
@@ -50,6 +109,39 @@ struct Metadata {
version: String,
}
+impl Metadata {
+ fn new(
+ specifier: &ModuleSpecifier,
+ source: &str,
+ version: &str,
+ media_type: &MediaType,
+ maybe_import_map: &Option<ImportMap>,
+ ) -> Self {
+ let (dependencies, maybe_types) = if let Some((dependencies, maybe_types)) =
+ analysis::analyze_dependencies(
+ specifier,
+ source,
+ media_type,
+ maybe_import_map,
+ ) {
+ (Some(dependencies), maybe_types)
+ } else {
+ (None, None)
+ };
+ let line_index = LineIndex::new(source);
+
+ Self {
+ dependencies,
+ length_utf16: source.encode_utf16().count(),
+ line_index,
+ maybe_types,
+ media_type: media_type.to_owned(),
+ source: source.to_string(),
+ version: version.to_string(),
+ }
+ }
+}
+
#[derive(Debug, Clone, Default)]
struct Inner {
http_cache: HttpCache,
@@ -71,6 +163,10 @@ impl Sources {
self.0.lock().unwrap().contains_key(specifier)
}
+ /// Provides the length of the source content, calculated in a way that should
+ /// match the behavior of JavaScript, where strings are stored effectively as
+ /// `&[u16]` and when counting "chars" we need to represent the string as a
+ /// UTF-16 string in Rust.
pub fn get_length_utf16(&self, specifier: &ModuleSpecifier) -> Option<usize> {
self.0.lock().unwrap().get_length_utf16(specifier)
}
@@ -103,8 +199,8 @@ impl Sources {
self.0.lock().unwrap().get_script_version(specifier)
}
- pub fn get_text(&self, specifier: &ModuleSpecifier) -> Option<String> {
- self.0.lock().unwrap().get_text(specifier)
+ pub fn get_source(&self, specifier: &ModuleSpecifier) -> Option<String> {
+ self.0.lock().unwrap().get_source(specifier)
}
pub fn resolve_import(
@@ -114,14 +210,6 @@ impl Sources {
) -> Option<(ModuleSpecifier, MediaType)> {
self.0.lock().unwrap().resolve_import(specifier, referrer)
}
-
- #[cfg(test)]
- fn resolve_specifier(
- &self,
- specifier: &ModuleSpecifier,
- ) -> Option<ModuleSpecifier> {
- self.0.lock().unwrap().resolve_specifier(specifier)
- }
}
impl Inner {
@@ -132,8 +220,27 @@ impl Inner {
}
}
+ fn calculate_script_version(
+ &mut self,
+ specifier: &ModuleSpecifier,
+ ) -> Option<String> {
+ let path = self.get_path(specifier)?;
+ let metadata = fs::metadata(path).ok()?;
+ if let Ok(modified) = metadata.modified() {
+ if let Ok(n) = modified.duration_since(SystemTime::UNIX_EPOCH) {
+ Some(format!("{}", n.as_millis()))
+ } else {
+ Some("1".to_string())
+ }
+ } else {
+ Some("1".to_string())
+ }
+ }
+
fn contains_key(&mut self, specifier: &ModuleSpecifier) -> bool {
- if let Some(specifier) = self.resolve_specifier(specifier) {
+ if let Some(specifier) =
+ resolve_specifier(specifier, &mut self.redirects, &self.http_cache)
+ {
if self.get_metadata(&specifier).is_some() {
return true;
}
@@ -141,21 +248,19 @@ impl Inner {
false
}
- /// Provides the length of the source content, calculated in a way that should
- /// match the behavior of JavaScript, where strings are stored effectively as
- /// `&[u16]` and when counting "chars" we need to represent the string as a
- /// UTF-16 string in Rust.
fn get_length_utf16(&mut self, specifier: &ModuleSpecifier) -> Option<usize> {
- let specifier = self.resolve_specifier(specifier)?;
+ let specifier =
+ resolve_specifier(specifier, &mut self.redirects, &self.http_cache)?;
let metadata = self.get_metadata(&specifier)?;
- Some(metadata.source.encode_utf16().count())
+ Some(metadata.length_utf16)
}
fn get_line_index(
&mut self,
specifier: &ModuleSpecifier,
) -> Option<LineIndex> {
- let specifier = self.resolve_specifier(specifier)?;
+ let specifier =
+ resolve_specifier(specifier, &mut self.redirects, &self.http_cache)?;
let metadata = self.get_metadata(&specifier)?;
Some(metadata.line_index)
}
@@ -164,7 +269,9 @@ impl Inner {
&mut self,
specifier: &ModuleSpecifier,
) -> Option<analysis::ResolvedDependency> {
- let metadata = self.get_metadata(specifier)?;
+ let specifier =
+ resolve_specifier(specifier, &mut self.redirects, &self.http_cache)?;
+ let metadata = self.get_metadata(&specifier)?;
metadata.maybe_types
}
@@ -172,118 +279,62 @@ impl Inner {
&mut self,
specifier: &ModuleSpecifier,
) -> Option<MediaType> {
- let specifier = self.resolve_specifier(specifier)?;
+ let specifier =
+ resolve_specifier(specifier, &mut self.redirects, &self.http_cache)?;
let metadata = self.get_metadata(&specifier)?;
Some(metadata.media_type)
}
fn get_metadata(&mut self, specifier: &ModuleSpecifier) -> Option<Metadata> {
if let Some(metadata) = self.metadata.get(specifier).cloned() {
- if metadata.version == self.get_script_version(specifier)? {
+ if metadata.version == self.calculate_script_version(specifier)? {
return Some(metadata);
}
}
- // TODO(@kitsonk) this needs to be refactored, lots of duplicate logic and
- // is really difficult to follow.
- let version = self.get_script_version(specifier)?;
+ let version = self.calculate_script_version(specifier)?;
let path = self.get_path(specifier)?;
- if let Ok(bytes) = fs::read(path) {
- if specifier.as_url().scheme() == "file" {
- let charset = text_encoding::detect_charset(&bytes).to_string();
- if let Ok(source) = get_source_from_bytes(bytes, Some(charset)) {
- let media_type = MediaType::from(specifier);
- let mut maybe_types = None;
- let maybe_import_map = self.maybe_import_map.clone();
- let dependencies = if let Some((dependencies, mt)) =
- analysis::analyze_dependencies(
- &specifier,
- &source,
- &media_type,
- &maybe_import_map,
- ) {
- maybe_types = mt;
- Some(dependencies)
- } else {
- None
- };
- let line_index = LineIndex::new(&source);
- let metadata = Metadata {
- dependencies,
- line_index,
- maybe_types,
- media_type,
- source,
- version,
- };
- self.metadata.insert(specifier.clone(), metadata.clone());
- Some(metadata)
- } else {
- None
- }
- } else {
- let headers = self.get_remote_headers(specifier)?;
- let maybe_content_type = headers.get("content-type").cloned();
- let (media_type, maybe_charset) =
- map_content_type(specifier, maybe_content_type);
- if let Ok(source) = get_source_from_bytes(bytes, maybe_charset) {
- let mut maybe_types =
- if let Some(types) = headers.get("x-typescript-types") {
- Some(analysis::resolve_import(
- types,
- &specifier,
- &self.maybe_import_map,
- ))
- } else {
- None
- };
- let maybe_import_map = self.maybe_import_map.clone();
- let dependencies = if let Some((dependencies, mt)) =
- analysis::analyze_dependencies(
- &specifier,
- &source,
- &media_type,
- &maybe_import_map,
- ) {
- if maybe_types.is_none() {
- maybe_types = mt;
- }
- Some(dependencies)
- } else {
- None
- };
- let line_index = LineIndex::new(&source);
- let metadata = Metadata {
- dependencies,
- line_index,
- maybe_types,
- media_type,
- source,
- version,
- };
- self.metadata.insert(specifier.clone(), metadata.clone());
- Some(metadata)
- } else {
- None
- }
- }
+ let bytes = fs::read(path).ok()?;
+ let scheme = specifier.as_url().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 {
- None
+ let cache_filename =
+ self.http_cache.get_cache_filename(specifier.as_url())?;
+ let headers = get_remote_headers(&cache_filename)?;
+ let maybe_content_type = headers.get("content-type").cloned();
+ let (media_type, maybe_charset) =
+ map_content_type(specifier, maybe_content_type);
+ let source = get_source_from_bytes(bytes, maybe_charset).ok()?;
+ let maybe_types = headers.get("x-typescript-types").map(|s| {
+ analysis::resolve_import(s, &specifier, &self.maybe_import_map)
+ });
+ (source, media_type, maybe_types)
+ };
+ let mut metadata = Metadata::new(
+ specifier,
+ &source,
+ &version,
+ &media_type,
+ &self.maybe_import_map,
+ );
+ if metadata.maybe_types.is_none() {
+ metadata.maybe_types = maybe_types;
}
+ self.metadata.insert(specifier.clone(), metadata.clone());
+ Some(metadata)
}
fn get_path(&mut self, specifier: &ModuleSpecifier) -> Option<PathBuf> {
- let specifier = self.resolve_specifier(specifier)?;
if specifier.as_url().scheme() == "file" {
- if let Ok(path) = specifier.as_url().to_file_path() {
- Some(path)
- } else {
- None
- }
+ specifier.as_url().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.as_url())?;
if path.is_file() {
self.remotes.insert(specifier.clone(), path.clone());
Some(path)
@@ -293,57 +344,35 @@ impl Inner {
}
}
- fn get_remote_headers(
- &self,
- specifier: &ModuleSpecifier,
- ) -> Option<HashMap<String, String>> {
- let cache_filename = self.http_cache.get_cache_filename(specifier.as_url());
- let metadata_path = http_cache::Metadata::filename(&cache_filename);
- if let Ok(metadata) = fs::read_to_string(metadata_path) {
- if let Ok(metadata) =
- serde_json::from_str::<'_, http_cache::Metadata>(&metadata)
- {
- return Some(metadata.headers);
- }
- }
- None
- }
-
fn get_script_version(
&mut self,
specifier: &ModuleSpecifier,
) -> Option<String> {
- let path = self.get_path(specifier)?;
- let metadata = fs::metadata(path).ok()?;
- if let Ok(modified) = metadata.modified() {
- if let Ok(n) = modified.duration_since(SystemTime::UNIX_EPOCH) {
- Some(format!("{}", n.as_millis()))
- } else {
- Some("1".to_string())
- }
- } else {
- Some("1".to_string())
- }
+ let specifier =
+ resolve_specifier(specifier, &mut self.redirects, &self.http_cache)?;
+ let metadata = self.get_metadata(&specifier)?;
+ Some(metadata.version)
}
- fn get_text(&mut self, specifier: &ModuleSpecifier) -> Option<String> {
- let specifier = self.resolve_specifier(specifier)?;
+ fn get_source(&mut self, specifier: &ModuleSpecifier) -> Option<String> {
+ let specifier =
+ resolve_specifier(specifier, &mut self.redirects, &self.http_cache)?;
let metadata = self.get_metadata(&specifier)?;
Some(metadata.source)
}
fn resolution_result(
&mut self,
- resolved_specifier: &ModuleSpecifier,
+ specifier: &ModuleSpecifier,
) -> Option<(ModuleSpecifier, MediaType)> {
- let resolved_specifier = self.resolve_specifier(resolved_specifier)?;
- let media_type =
- if let Some(metadata) = self.metadata.get(&resolved_specifier) {
- metadata.media_type
- } else {
- MediaType::from(&resolved_specifier)
- };
- Some((resolved_specifier, media_type))
+ let specifier =
+ resolve_specifier(specifier, &mut self.redirects, &self.http_cache)?;
+ let media_type = if let Some(metadata) = self.metadata.get(&specifier) {
+ metadata.media_type
+ } else {
+ MediaType::from(&specifier)
+ };
+ Some((specifier, media_type))
}
fn resolve_import(
@@ -351,7 +380,8 @@ impl Inner {
specifier: &str,
referrer: &ModuleSpecifier,
) -> Option<(ModuleSpecifier, MediaType)> {
- let referrer = self.resolve_specifier(referrer)?;
+ let referrer =
+ resolve_specifier(referrer, &mut self.redirects, &self.http_cache)?;
let metadata = self.get_metadata(&referrer)?;
let dependencies = &metadata.dependencies?;
let dependency = dependencies.get(specifier)?;
@@ -368,62 +398,38 @@ impl Inner {
if let analysis::ResolvedDependency::Resolved(resolved_specifier) =
code_dependency
{
- self.resolution_result(resolved_specifier)
+ if let Some(type_dependency) = self.get_maybe_types(resolved_specifier)
+ {
+ self.set_maybe_type(specifier, &referrer, &type_dependency);
+ if let analysis::ResolvedDependency::Resolved(type_specifier) =
+ type_dependency
+ {
+ self.resolution_result(&type_specifier)
+ } else {
+ self.resolution_result(resolved_specifier)
+ }
+ } else {
+ self.resolution_result(resolved_specifier)
+ }
} else {
None
}
}
}
- fn resolve_specifier(
+ fn set_maybe_type(
&mut self,
- specifier: &ModuleSpecifier,
- ) -> Option<ModuleSpecifier> {
- let scheme = specifier.as_url().scheme();
- if !SUPPORTED_SCHEMES.contains(&scheme) {
- return None;
- }
-
- if scheme == "file" {
- if let Ok(path) = specifier.as_url().to_file_path() {
- if path.is_file() {
- return Some(specifier.clone());
- }
- }
- } else {
- if let Some(specifier) = self.redirects.get(specifier) {
- return Some(specifier.clone());
- }
- if let Some(redirect) = self.resolve_remote_specifier(specifier, 10) {
- self.redirects.insert(specifier.clone(), redirect.clone());
- return Some(redirect);
- }
- }
- None
- }
-
- fn resolve_remote_specifier(
- &self,
- specifier: &ModuleSpecifier,
- redirect_limit: isize,
- ) -> Option<ModuleSpecifier> {
- let cached_filename =
- self.http_cache.get_cache_filename(specifier.as_url());
- if redirect_limit >= 0 && cached_filename.is_file() {
- if let Some(headers) = self.get_remote_headers(specifier) {
- if let Some(redirect_to) = headers.get("location") {
- if let Ok(redirect) =
- ModuleSpecifier::resolve_import(redirect_to, specifier.as_str())
- {
- return self
- .resolve_remote_specifier(&redirect, redirect_limit - 1);
- }
- } else {
- return Some(specifier.clone());
+ specifier: &str,
+ referrer: &ModuleSpecifier,
+ dependency: &analysis::ResolvedDependency,
+ ) {
+ if let Some(metadata) = self.metadata.get_mut(referrer) {
+ if let Some(dependencies) = &mut metadata.dependencies {
+ if let Some(dep) = dependencies.get_mut(specifier) {
+ dep.maybe_type = Some(dependency.clone());
}
}
}
- None
}
}
@@ -462,7 +468,7 @@ mod tests {
&tests.join("001_hello.js").to_string_lossy(),
)
.unwrap();
- let actual = sources.get_text(&specifier);
+ let actual = sources.get_source(&specifier);
assert!(actual.is_some());
let actual = actual.unwrap();
assert_eq!(actual, "console.log(\"Hello World\");\n");
@@ -484,11 +490,78 @@ mod tests {
}
#[test]
+ 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();
+ cache
+ .set(
+ specifier_dep.as_url(),
+ 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 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;",
+ )
+ .unwrap();
+ let specifier_type =
+ ModuleSpecifier::resolve_url("https://deno.land/x/lib.d.ts").unwrap();
+ cache
+ .set(
+ specifier_type.as_url(),
+ Default::default(),
+ b"export const a: number;",
+ )
+ .unwrap();
+ let actual =
+ sources.resolve_import("https://deno.land/x/lib.js", &specifier_dep);
+ assert_eq!(actual, Some((specifier_type, MediaType::Dts)))
+ }
+
+ #[test]
+ 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 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(
+ remote_specifier.as_url(),
+ Default::default(),
+ b"export * from \"./evil.ts\";",
+ )
+ .unwrap();
+ let actual = sources.resolve_import("./evil.ts", &remote_specifier);
+ assert_eq!(actual, None);
+ }
+
+ #[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 actual = sources.resolve_specifier(&specifier);
+ let sources = sources.0.lock().unwrap();
+ let mut redirects = sources.redirects.clone();
+ let http_cache = sources.http_cache.clone();
+ let actual = resolve_specifier(&specifier, &mut redirects, &http_cache);
assert!(actual.is_none());
}
}
diff --git a/cli/lsp/tsc.rs b/cli/lsp/tsc.rs
index f3a83d544..ef57682f1 100644
--- a/cli/lsp/tsc.rs
+++ b/cli/lsp/tsc.rs
@@ -1117,8 +1117,7 @@ fn get_text(state: &mut State, args: Value) -> Result<Value, AnyError> {
.unwrap()
.clone()
} else {
- let sources = &mut state.state_snapshot.sources;
- sources.get_text(&specifier).unwrap()
+ state.state_snapshot.sources.get_source(&specifier).unwrap()
};
state.state_snapshot.performance.measure(mark);
Ok(json!(text::slice(&content, v.start..v.end)))