summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cli/jsr.rs6
-rw-r--r--cli/lsp/analysis.rs5
-rw-r--r--cli/lsp/diagnostics.rs4
-rw-r--r--cli/lsp/documents.rs184
-rw-r--r--cli/lsp/language_server.rs44
-rw-r--r--cli/lsp/resolver.rs170
-rw-r--r--cli/lsp/tsc.rs8
7 files changed, 222 insertions, 199 deletions
diff --git a/cli/jsr.rs b/cli/jsr.rs
index 8b13f5893..c4cb87dbd 100644
--- a/cli/jsr.rs
+++ b/cli/jsr.rs
@@ -163,6 +163,12 @@ impl JsrCacheResolver {
self.info_by_nv.insert(nv.clone(), info.clone());
info
}
+
+ pub fn did_cache(&self) {
+ self.nv_by_req.retain(|_, nv| nv.is_some());
+ self.info_by_nv.retain(|_, info| info.is_some());
+ self.info_by_name.retain(|_, info| info.is_some());
+ }
}
fn read_cached_url(
diff --git a/cli/lsp/analysis.rs b/cli/lsp/analysis.rs
index 63d39ad6e..23b6bb099 100644
--- a/cli/lsp/analysis.rs
+++ b/cli/lsp/analysis.rs
@@ -259,8 +259,7 @@ impl<'a> TsResponseImportMapper<'a> {
let version = Version::parse_standard(segments.next()?).ok()?;
let nv = PackageNv { name, version };
let path = segments.collect::<Vec<_>>().join("/");
- let jsr_resolver = self.documents.get_jsr_resolver();
- let export = jsr_resolver.lookup_export_for_path(&nv, &path)?;
+ let export = self.resolver.jsr_lookup_export_for_path(&nv, &path)?;
let sub_path = (export != ".").then_some(export);
let mut req = None;
req = req.or_else(|| {
@@ -282,7 +281,7 @@ impl<'a> TsResponseImportMapper<'a> {
}
None
});
- req = req.or_else(|| jsr_resolver.lookup_req_for_nv(&nv));
+ req = req.or_else(|| self.resolver.jsr_lookup_req_for_nv(&nv));
let spec_str = if let Some(req) = req {
let req_ref = PackageReqReference { req, sub_path };
JsrPackageReqReference::new(req_ref).to_string()
diff --git a/cli/lsp/diagnostics.rs b/cli/lsp/diagnostics.rs
index 87bb72d1e..1825a97a4 100644
--- a/cli/lsp/diagnostics.rs
+++ b/cli/lsp/diagnostics.rs
@@ -1591,7 +1591,7 @@ mod tests {
location.to_path_buf(),
RealDenoCacheEnv,
));
- let mut documents = Documents::new(cache);
+ let mut documents = Documents::new(cache.clone());
for (specifier, source, version, language_id) in fixtures {
let specifier =
resolve_url(specifier).expect("failed to create specifier");
@@ -1614,7 +1614,7 @@ mod tests {
config.tree.inject_config_file(config_file).await;
}
let resolver = LspResolver::default()
- .with_new_config(&config, None, None)
+ .with_new_config(&config, cache, None, None)
.await;
StateSnapshot {
project_version: 0,
diff --git a/cli/lsp/documents.rs b/cli/lsp/documents.rs
index ee7adc83a..d008dbb74 100644
--- a/cli/lsp/documents.rs
+++ b/cli/lsp/documents.rs
@@ -12,7 +12,6 @@ use super::tsc::AssetDocument;
use crate::cache::HttpCache;
use crate::graph_util::CliJsrUrlProvider;
-use crate::jsr::JsrCacheResolver;
use crate::lsp::logging::lsp_warn;
use crate::resolver::SloppyImportsFsEntry;
use crate::resolver::SloppyImportsResolution;
@@ -32,9 +31,7 @@ use deno_core::futures::FutureExt;
use deno_core::parking_lot::Mutex;
use deno_core::ModuleSpecifier;
use deno_graph::source::ResolutionMode;
-use deno_graph::GraphImport;
use deno_graph::Resolution;
-use deno_lockfile::Lockfile;
use deno_runtime::deno_node;
use deno_runtime::deno_node::NodeResolution;
use deno_runtime::deno_node::NodeResolutionMode;
@@ -705,64 +702,6 @@ pub fn to_lsp_range(range: &deno_graph::Range) -> lsp::Range {
}
}
-#[derive(Debug)]
-struct RedirectResolver {
- cache: Arc<dyn HttpCache>,
- redirects: Mutex<HashMap<ModuleSpecifier, ModuleSpecifier>>,
-}
-
-impl RedirectResolver {
- pub fn new(cache: Arc<dyn HttpCache>) -> Self {
- Self {
- cache,
- redirects: Mutex::new(HashMap::new()),
- }
- }
-
- pub fn resolve(
- &self,
- specifier: &ModuleSpecifier,
- ) -> Option<ModuleSpecifier> {
- let scheme = specifier.scheme();
- if !DOCUMENT_SCHEMES.contains(&scheme) {
- return None;
- }
-
- if scheme == "http" || scheme == "https" {
- let mut redirects = self.redirects.lock();
- if let Some(specifier) = redirects.get(specifier) {
- Some(specifier.clone())
- } else {
- let redirect = self.resolve_remote(specifier, 10)?;
- redirects.insert(specifier.clone(), redirect.clone());
- Some(redirect)
- }
- } else {
- Some(specifier.clone())
- }
- }
-
- fn resolve_remote(
- &self,
- specifier: &ModuleSpecifier,
- redirect_limit: usize,
- ) -> Option<ModuleSpecifier> {
- if redirect_limit > 0 {
- let cache_key = self.cache.cache_item_key(specifier).ok()?;
- let headers = self.cache.read_headers(&cache_key).ok().flatten()?;
- if let Some(location) = headers.get("location") {
- let redirect =
- deno_core::resolve_import(location, specifier.as_str()).ok()?;
- self.resolve_remote(&redirect, redirect_limit - 1)
- } else {
- Some(specifier.clone())
- }
- } else {
- None
- }
- }
-}
-
#[derive(Debug, Default)]
struct FileSystemDocuments {
docs: DashMap<ModuleSpecifier, Arc<Document>>,
@@ -907,21 +846,14 @@ pub struct Documents {
open_docs: HashMap<ModuleSpecifier, Arc<Document>>,
/// Documents stored on the file system.
file_system_docs: Arc<FileSystemDocuments>,
- /// Any imports to the context supplied by configuration files. This is like
- /// the imports into the a module graph in CLI.
- imports: Arc<IndexMap<ModuleSpecifier, GraphImport>>,
/// A resolver that takes into account currently loaded import map and JSX
/// settings.
resolver: Arc<LspResolver>,
- jsr_resolver: Arc<JsrCacheResolver>,
- lockfile: Option<Arc<Mutex<Lockfile>>>,
/// The npm package requirements found in npm specifiers.
npm_specifier_reqs: Arc<Vec<PackageReq>>,
/// Gets if any document had a node: specifier such that a @types/node package
/// should be injected.
has_injected_types_node_package: bool,
- /// Resolves a specifier to its final redirected to specifier.
- redirect_resolver: Arc<RedirectResolver>,
/// If --unstable-sloppy-imports is enabled.
unstable_sloppy_imports: bool,
}
@@ -934,29 +866,13 @@ impl Documents {
dirty: true,
open_docs: HashMap::default(),
file_system_docs: Default::default(),
- imports: Default::default(),
resolver: Default::default(),
- jsr_resolver: Arc::new(JsrCacheResolver::new(cache.clone(), None)),
- lockfile: None,
npm_specifier_reqs: Default::default(),
has_injected_types_node_package: false,
- redirect_resolver: Arc::new(RedirectResolver::new(cache)),
unstable_sloppy_imports: false,
}
}
- pub fn initialize(&mut self, config: &Config) {
- self.config = Arc::new(config.clone());
- }
-
- pub fn module_graph_imports(&self) -> impl Iterator<Item = &ModuleSpecifier> {
- self
- .imports
- .values()
- .flat_map(|i| i.dependencies.values())
- .flat_map(|value| value.get_type().or_else(|| value.get_code()))
- }
-
/// "Open" a document from the perspective of the editor, meaning that
/// requests for information from the document will come from the in-memory
/// representation received from the language server client, versus reading
@@ -1091,11 +1007,14 @@ impl Documents {
let specifier = if let Ok(jsr_req_ref) =
JsrPackageReqReference::from_specifier(specifier)
{
- Cow::Owned(self.jsr_resolver.jsr_to_registry_url(&jsr_req_ref)?)
+ Cow::Owned(self.resolver.jsr_to_registry_url(&jsr_req_ref)?)
} else {
Cow::Borrowed(specifier)
};
- self.redirect_resolver.resolve(&specifier)
+ if !DOCUMENT_SCHEMES.contains(&specifier.scheme()) {
+ return None;
+ }
+ self.resolver.resolve_redirects(&specifier)
}
}
@@ -1268,7 +1187,8 @@ impl Documents {
results.push(None);
}
} else if let Some(specifier) = self
- .resolve_imports_dependency(specifier)
+ .resolver
+ .resolve_graph_import(specifier)
.and_then(|r| r.maybe_specifier())
{
results.push(self.resolve_dependency(specifier, referrer));
@@ -1297,62 +1217,18 @@ impl Documents {
results
}
- /// Update the location of the on disk cache for the document store.
- pub fn set_cache(&mut self, cache: Arc<dyn HttpCache>) {
- // TODO update resolved dependencies?
- self.cache = cache.clone();
- self.redirect_resolver = Arc::new(RedirectResolver::new(cache));
- self.dirty = true;
- }
-
- pub fn get_jsr_resolver(&self) -> &Arc<JsrCacheResolver> {
- &self.jsr_resolver
- }
-
- pub fn refresh_lockfile(&mut self, lockfile: Option<Arc<Mutex<Lockfile>>>) {
- self.jsr_resolver =
- Arc::new(JsrCacheResolver::new(self.cache.clone(), lockfile.clone()));
- self.lockfile = lockfile;
- }
-
pub fn update_config(
&mut self,
config: &Config,
resolver: &Arc<LspResolver>,
+ cache: Arc<dyn HttpCache>,
workspace_files: &BTreeSet<ModuleSpecifier>,
) {
self.config = Arc::new(config.clone());
+ self.cache = cache;
let config_data = config.tree.root_data();
let config_file = config_data.and_then(|d| d.config_file.as_deref());
self.resolver = resolver.clone();
- self.jsr_resolver = Arc::new(JsrCacheResolver::new(
- self.cache.clone(),
- config.tree.root_lockfile().cloned(),
- ));
- self.lockfile = config.tree.root_lockfile().cloned();
- self.redirect_resolver =
- Arc::new(RedirectResolver::new(self.cache.clone()));
- let graph_resolver = self.resolver.as_graph_resolver();
- let npm_resolver = self.resolver.as_graph_npm_resolver();
- self.imports = Arc::new(
- if let Some(Ok(imports)) = config_file.map(|cf| cf.to_maybe_imports()) {
- imports
- .into_iter()
- .map(|(referrer, imports)| {
- let graph_import = GraphImport::new(
- &referrer,
- imports,
- &CliJsrUrlProvider,
- Some(graph_resolver),
- Some(npm_resolver),
- );
- (referrer, graph_import)
- })
- .collect()
- } else {
- IndexMap::new()
- },
- );
self.unstable_sloppy_imports = config_file
.map(|c| c.has_unstable("sloppy-imports"))
.unwrap_or(false);
@@ -1450,7 +1326,7 @@ impl Documents {
}
// fill the reqs from the lockfile
- if let Some(lockfile) = self.lockfile.as_ref() {
+ if let Some(lockfile) = self.config.tree.root_lockfile() {
let lockfile = lockfile.lock();
for key in lockfile.content.packages.specifiers.keys() {
if let Some(key) = key.strip_prefix("npm:") {
@@ -1505,19 +1381,6 @@ impl Documents {
Some((doc.specifier().clone(), media_type))
}
}
-
- /// Iterate through any "imported" modules, checking to see if a dependency
- /// is available. This is used to provide "global" imports like the JSX import
- /// source.
- fn resolve_imports_dependency(&self, specifier: &str) -> Option<&Resolution> {
- for graph_imports in self.imports.values() {
- let maybe_dep = graph_imports.dependencies.get(specifier);
- if maybe_dep.is_some() {
- return maybe_dep.map(|d| &d.maybe_type);
- }
- }
- None
- }
}
fn node_resolve_npm_req_ref(
@@ -1691,20 +1554,20 @@ mod tests {
use test_util::PathRef;
use test_util::TempDir;
- fn setup(temp_dir: &TempDir) -> (Documents, PathRef) {
+ fn setup(temp_dir: &TempDir) -> (Documents, PathRef, Arc<dyn HttpCache>) {
let location = temp_dir.path().join("deps");
let cache = Arc::new(GlobalHttpCache::new(
location.to_path_buf(),
RealDenoCacheEnv,
));
- let documents = Documents::new(cache);
- (documents, location)
+ let documents = Documents::new(cache.clone());
+ (documents, location, cache)
}
#[test]
fn test_documents_open_close() {
let temp_dir = TempDir::new();
- let (mut documents, _) = setup(&temp_dir);
+ let (mut documents, _, _) = setup(&temp_dir);
let specifier = ModuleSpecifier::parse("file:///a.ts").unwrap();
let content = r#"import * as b from "./b.ts";
console.log(b);
@@ -1730,7 +1593,7 @@ console.log(b);
#[test]
fn test_documents_change() {
let temp_dir = TempDir::new();
- let (mut documents, _) = setup(&temp_dir);
+ let (mut documents, _, _) = setup(&temp_dir);
let specifier = ModuleSpecifier::parse("file:///a.ts").unwrap();
let content = r#"import * as b from "./b.ts";
console.log(b);
@@ -1774,7 +1637,7 @@ console.log(b, "hello deno");
// it should never happen that a user of this API causes this to happen,
// but we'll guard against it anyway
let temp_dir = TempDir::new();
- let (mut documents, documents_path) = setup(&temp_dir);
+ let (mut documents, documents_path, _) = setup(&temp_dir);
let file_path = documents_path.join("file.ts");
let file_specifier = ModuleSpecifier::from_file_path(&file_path).unwrap();
documents_path.create_dir_all();
@@ -1802,7 +1665,7 @@ console.log(b, "hello deno");
// it should never happen that a user of this API causes this to happen,
// but we'll guard against it anyway
let temp_dir = TempDir::new();
- let (mut documents, documents_path) = setup(&temp_dir);
+ let (mut documents, documents_path, cache) = setup(&temp_dir);
fs::create_dir_all(&documents_path).unwrap();
let file1_path = documents_path.join("file1.ts");
@@ -1851,9 +1714,14 @@ console.log(b, "hello deno");
.await;
let resolver = LspResolver::default()
- .with_new_config(&config, None, None)
+ .with_new_config(&config, cache.clone(), None, None)
.await;
- documents.update_config(&config, &resolver, &workspace_files);
+ documents.update_config(
+ &config,
+ &resolver,
+ cache.clone(),
+ &workspace_files,
+ );
// open the document
let document = documents.open(
@@ -1895,9 +1763,9 @@ console.log(b, "hello deno");
.await;
let resolver = LspResolver::default()
- .with_new_config(&config, None, None)
+ .with_new_config(&config, cache.clone(), None, None)
.await;
- documents.update_config(&config, &resolver, &workspace_files);
+ documents.update_config(&config, &resolver, cache, &workspace_files);
// check the document's dependencies
let document = documents.get(&file1_specifier).unwrap();
diff --git a/cli/lsp/language_server.rs b/cli/lsp/language_server.rs
index 5879a7491..f7b509a1c 100644
--- a/cli/lsp/language_server.rs
+++ b/cli/lsp/language_server.rs
@@ -316,10 +316,17 @@ impl LanguageServer {
// now get the lock back to update with the new information
let mut inner = self.0.write().await;
- let lockfile = inner.config.tree.root_lockfile().cloned();
- inner.documents.refresh_lockfile(lockfile);
+ inner.resolver.did_cache();
inner.refresh_npm_specifiers().await;
- inner.post_cache(result.mark).await;
+ inner.diagnostics_server.invalidate_all();
+ inner.project_changed([], false);
+ inner
+ .ts_server
+ .cleanup_semantic_cache(inner.snapshot())
+ .await;
+ inner.send_diagnostics_update();
+ inner.send_testing_update();
+ inner.performance.measure(result.mark);
}
Ok(Some(json!(true)))
}
@@ -689,7 +696,6 @@ impl Inner {
.clone()
.map(|c| c as Arc<dyn HttpCache>)
.unwrap_or(self.global_cache.clone());
- self.documents.set_cache(self.cache.clone());
self.cache_metadata.set_cache(self.cache.clone());
self.performance.measure(mark);
}
@@ -781,8 +787,6 @@ impl Inner {
self.config.update_capabilities(&params.capabilities);
}
- self.documents.initialize(&self.config);
-
if let Err(e) = self
.ts_server
.start(self.config.internal_inspect().to_address())
@@ -986,10 +990,14 @@ impl Inner {
}
}
}
+ }
+
+ async fn refresh_resolver(&mut self) {
self.resolver = self
.resolver
.with_new_config(
&self.config,
+ self.cache.clone(),
self.maybe_global_cache_path.as_deref(),
Some(&self.http_client),
)
@@ -1000,6 +1008,7 @@ impl Inner {
self.documents.update_config(
&self.config,
&self.resolver,
+ self.cache.clone(),
&self.workspace_files,
);
@@ -1133,6 +1142,7 @@ impl Inner {
self.refresh_workspace_files();
self.refresh_config_tree().await;
self.update_cache();
+ self.refresh_resolver().await;
self.refresh_documents_config().await;
self.diagnostics_server.invalidate_all();
self.send_diagnostics_update();
@@ -1181,6 +1191,7 @@ impl Inner {
self.workspace_files_hash = 0;
self.refresh_workspace_files();
self.refresh_config_tree().await;
+ self.refresh_resolver().await;
deno_config_changes.extend(changes.iter().filter_map(|(s, e)| {
self.config.tree.watched_file_type(s).and_then(|t| {
let configuration_type = match t.1 {
@@ -1482,10 +1493,7 @@ impl Inner {
if let Ok(jsr_req_ref) =
JsrPackageReqReference::from_specifier(specifier)
{
- if let Some(url) = self
- .documents
- .get_jsr_resolver()
- .jsr_to_registry_url(&jsr_req_ref)
+ if let Some(url) = self.resolver.jsr_to_registry_url(&jsr_req_ref)
{
result = format!("{result} (<{url}>)");
}
@@ -2961,6 +2969,7 @@ impl tower_lsp::LanguageServer for LanguageServer {
{
let mut ls = self.0.write().await;
init_log_file(ls.config.log_file());
+ ls.refresh_resolver().await;
ls.refresh_documents_config().await;
ls.diagnostics_server.invalidate_all();
ls.send_diagnostics_update();
@@ -3095,6 +3104,7 @@ impl tower_lsp::LanguageServer for LanguageServer {
let mut ls = self.0.write().await;
ls.refresh_workspace_files();
ls.refresh_config_tree().await;
+ ls.refresh_resolver().await;
ls.refresh_documents_config().await;
ls.diagnostics_server.invalidate_all();
ls.send_diagnostics_update();
@@ -3354,20 +3364,6 @@ impl Inner {
}))
}
- async fn post_cache(&mut self, mark: PerformanceMark) {
- // Now that we have dependencies loaded, we need to re-analyze all the files.
- // For that we're invalidating all the existing diagnostics and restarting
- // the language server for TypeScript (as it might hold to some stale
- // documents).
- self.diagnostics_server.invalidate_all();
- self.project_changed([], false);
- self.ts_server.cleanup_semantic_cache(self.snapshot()).await;
- self.send_diagnostics_update();
- self.send_testing_update();
-
- self.performance.measure(mark);
- }
-
fn get_performance(&self) -> Value {
let averages = self.performance.averages();
json!({ "averages": averages })
diff --git a/cli/lsp/resolver.rs b/cli/lsp/resolver.rs
index 076d48bb4..ebe254b9d 100644
--- a/cli/lsp/resolver.rs
+++ b/cli/lsp/resolver.rs
@@ -4,7 +4,9 @@ use crate::args::package_json;
use crate::args::CacheSetting;
use crate::cache::DenoDir;
use crate::cache::FastInsecureHasher;
+use crate::graph_util::CliJsrUrlProvider;
use crate::http_util::HttpClient;
+use crate::jsr::JsrCacheResolver;
use crate::lsp::config::Config;
use crate::lsp::config::ConfigData;
use crate::lsp::logging::lsp_warn;
@@ -21,10 +23,14 @@ use crate::resolver::CliGraphResolverOptions;
use crate::resolver::CliNodeResolver;
use crate::util::progress_bar::ProgressBar;
use crate::util::progress_bar::ProgressBarStyle;
+use deno_cache_dir::HttpCache;
use deno_core::error::AnyError;
+use deno_core::parking_lot::Mutex;
use deno_graph::source::NpmResolver;
use deno_graph::source::Resolver;
+use deno_graph::GraphImport;
use deno_graph::ModuleSpecifier;
+use deno_graph::Resolution;
use deno_npm::NpmSystemInfo;
use deno_runtime::deno_fs;
use deno_runtime::deno_node::NodeResolution;
@@ -33,9 +39,13 @@ use deno_runtime::deno_node::NodeResolver;
use deno_runtime::deno_node::PackageJson;
use deno_runtime::fs_util::specifier_to_file_path;
use deno_runtime::permissions::PermissionsContainer;
+use deno_semver::jsr::JsrPackageReqReference;
use deno_semver::npm::NpmPackageReqReference;
+use deno_semver::package::PackageNv;
use deno_semver::package::PackageReq;
+use indexmap::IndexMap;
use package_json::PackageJsonDepsProvider;
+use std::collections::HashMap;
use std::path::Path;
use std::rc::Rc;
use std::sync::Arc;
@@ -43,19 +53,25 @@ use std::sync::Arc;
#[derive(Debug, Clone)]
pub struct LspResolver {
graph_resolver: Arc<CliGraphResolver>,
+ jsr_resolver: Option<Arc<JsrCacheResolver>>,
npm_resolver: Option<Arc<dyn CliNpmResolver>>,
node_resolver: Option<Arc<CliNodeResolver>>,
npm_config_hash: LspNpmConfigHash,
+ redirect_resolver: Option<Arc<RedirectResolver>>,
+ graph_imports: Arc<IndexMap<ModuleSpecifier, GraphImport>>,
config: Arc<Config>,
}
impl Default for LspResolver {
fn default() -> Self {
Self {
- graph_resolver: create_graph_resolver(&Default::default(), None, None),
+ graph_resolver: create_graph_resolver(None, None, None),
+ jsr_resolver: None,
npm_resolver: None,
node_resolver: None,
npm_config_hash: LspNpmConfigHash(0),
+ redirect_resolver: None,
+ graph_imports: Default::default(),
config: Default::default(),
}
}
@@ -65,15 +81,16 @@ impl LspResolver {
pub async fn with_new_config(
&self,
config: &Config,
+ cache: Arc<dyn HttpCache>,
global_cache_path: Option<&Path>,
http_client: Option<&Arc<HttpClient>>,
) -> Arc<Self> {
let npm_config_hash = LspNpmConfigHash::new(config, global_cache_path);
+ let config_data = config.tree.root_data();
let mut npm_resolver = None;
let mut node_resolver = None;
if npm_config_hash != self.npm_config_hash {
- if let (Some(http_client), Some(config_data)) =
- (http_client, config.tree.root_data())
+ if let (Some(http_client), Some(config_data)) = (http_client, config_data)
{
npm_resolver =
create_npm_resolver(config_data, global_cache_path, http_client)
@@ -85,15 +102,44 @@ impl LspResolver {
node_resolver = self.node_resolver.clone();
}
let graph_resolver = create_graph_resolver(
- config,
+ config_data,
npm_resolver.as_ref(),
node_resolver.as_ref(),
);
+ let jsr_resolver = Some(Arc::new(JsrCacheResolver::new(
+ cache.clone(),
+ config_data.and_then(|d| d.lockfile.clone()),
+ )));
+ let redirect_resolver = Some(Arc::new(RedirectResolver::new(cache)));
+ let graph_imports = config_data
+ .and_then(|d| d.config_file.as_ref())
+ .and_then(|cf| cf.to_maybe_imports().ok())
+ .map(|imports| {
+ Arc::new(
+ imports
+ .into_iter()
+ .map(|(referrer, imports)| {
+ let graph_import = GraphImport::new(
+ &referrer,
+ imports,
+ &CliJsrUrlProvider,
+ Some(graph_resolver.as_ref()),
+ Some(graph_resolver.as_ref()),
+ );
+ (referrer, graph_import)
+ })
+ .collect(),
+ )
+ })
+ .unwrap_or_default();
Arc::new(Self {
graph_resolver,
+ jsr_resolver,
npm_resolver,
node_resolver,
npm_config_hash,
+ redirect_resolver,
+ graph_imports,
config: Arc::new(config.clone()),
})
}
@@ -103,19 +149,26 @@ impl LspResolver {
self.npm_resolver.as_ref().map(|r| r.clone_snapshotted());
let node_resolver = create_node_resolver(npm_resolver.as_ref());
let graph_resolver = create_graph_resolver(
- &self.config,
+ self.config.tree.root_data(),
npm_resolver.as_ref(),
node_resolver.as_ref(),
);
Arc::new(Self {
graph_resolver,
+ jsr_resolver: self.jsr_resolver.clone(),
npm_resolver,
node_resolver,
npm_config_hash: self.npm_config_hash.clone(),
+ redirect_resolver: self.redirect_resolver.clone(),
+ graph_imports: self.graph_imports.clone(),
config: self.config.clone(),
})
}
+ pub fn did_cache(&self) {
+ self.jsr_resolver.as_ref().inspect(|r| r.did_cache());
+ }
+
pub async fn set_npm_package_reqs(
&self,
reqs: &[PackageReq],
@@ -136,10 +189,49 @@ impl LspResolver {
self.graph_resolver.as_ref()
}
+ pub fn jsr_to_registry_url(
+ &self,
+ req_ref: &JsrPackageReqReference,
+ ) -> Option<ModuleSpecifier> {
+ self.jsr_resolver.as_ref()?.jsr_to_registry_url(req_ref)
+ }
+
+ pub fn jsr_lookup_export_for_path(
+ &self,
+ nv: &PackageNv,
+ path: &str,
+ ) -> Option<String> {
+ self.jsr_resolver.as_ref()?.lookup_export_for_path(nv, path)
+ }
+
+ pub fn jsr_lookup_req_for_nv(&self, nv: &PackageNv) -> Option<PackageReq> {
+ self.jsr_resolver.as_ref()?.lookup_req_for_nv(nv)
+ }
+
pub fn maybe_managed_npm_resolver(&self) -> Option<&ManagedCliNpmResolver> {
self.npm_resolver.as_ref().and_then(|r| r.as_managed())
}
+ pub fn graph_import_specifiers(
+ &self,
+ ) -> impl Iterator<Item = &ModuleSpecifier> {
+ self
+ .graph_imports
+ .values()
+ .flat_map(|i| i.dependencies.values())
+ .flat_map(|value| value.get_type().or_else(|| value.get_code()))
+ }
+
+ pub fn resolve_graph_import(&self, specifier: &str) -> Option<&Resolution> {
+ for graph_imports in self.graph_imports.values() {
+ let maybe_dep = graph_imports.dependencies.get(specifier);
+ if maybe_dep.is_some() {
+ return maybe_dep.map(|d| &d.maybe_type);
+ }
+ }
+ None
+ }
+
pub fn in_npm_package(&self, specifier: &ModuleSpecifier) -> bool {
if let Some(npm_resolver) = &self.npm_resolver {
return npm_resolver.in_npm_package(specifier);
@@ -203,6 +295,16 @@ impl LspResolver {
node_resolver
.get_closest_package_json(referrer, &PermissionsContainer::allow_all())
}
+
+ pub fn resolve_redirects(
+ &self,
+ specifier: &ModuleSpecifier,
+ ) -> Option<ModuleSpecifier> {
+ let Some(redirect_resolver) = self.redirect_resolver.as_ref() else {
+ return Some(specifier.clone());
+ };
+ redirect_resolver.resolve(specifier)
+ }
}
async fn create_npm_resolver(
@@ -275,11 +377,10 @@ fn create_node_resolver(
}
fn create_graph_resolver(
- config: &Config,
+ config_data: Option<&ConfigData>,
npm_resolver: Option<&Arc<dyn CliNpmResolver>>,
node_resolver: Option<&Arc<CliNodeResolver>>,
) -> Arc<CliGraphResolver> {
- let config_data = config.tree.root_data();
let config_file = config_data.and_then(|d| d.config_file.as_deref());
Arc::new(CliGraphResolver::new(CliGraphResolverOptions {
node_resolver: node_resolver.cloned(),
@@ -296,7 +397,7 @@ fn create_graph_resolver(
maybe_import_map: config_data.and_then(|d| d.import_map.clone()),
maybe_vendor_dir: config_data.and_then(|d| d.vendor_dir.as_ref()),
bare_node_builtins_enabled: config_file
- .map(|config| config.has_unstable("bare-node-builtins"))
+ .map(|cf| cf.has_unstable("bare-node-builtins"))
.unwrap_or(false),
// Don't set this for the LSP because instead we'll use the OpenDocumentsLoader
// because it's much easier and we get diagnostics/quick fixes about a redirected
@@ -326,3 +427,56 @@ impl LspNpmConfigHash {
Self(hasher.finish())
}
}
+
+#[derive(Debug)]
+struct RedirectResolver {
+ cache: Arc<dyn HttpCache>,
+ redirects: Mutex<HashMap<ModuleSpecifier, ModuleSpecifier>>,
+}
+
+impl RedirectResolver {
+ pub fn new(cache: Arc<dyn HttpCache>) -> Self {
+ Self {
+ cache,
+ redirects: Mutex::new(HashMap::new()),
+ }
+ }
+
+ pub fn resolve(
+ &self,
+ specifier: &ModuleSpecifier,
+ ) -> Option<ModuleSpecifier> {
+ if matches!(specifier.scheme(), "http" | "https") {
+ let mut redirects = self.redirects.lock();
+ if let Some(specifier) = redirects.get(specifier) {
+ Some(specifier.clone())
+ } else {
+ let redirect = self.resolve_remote(specifier, 10)?;
+ redirects.insert(specifier.clone(), redirect.clone());
+ Some(redirect)
+ }
+ } else {
+ Some(specifier.clone())
+ }
+ }
+
+ fn resolve_remote(
+ &self,
+ specifier: &ModuleSpecifier,
+ redirect_limit: usize,
+ ) -> Option<ModuleSpecifier> {
+ if redirect_limit > 0 {
+ let cache_key = self.cache.cache_item_key(specifier).ok()?;
+ let headers = self.cache.read_headers(&cache_key).ok().flatten()?;
+ if let Some(location) = headers.get("location") {
+ let redirect =
+ deno_core::resolve_import(location, specifier.as_str()).ok()?;
+ self.resolve_remote(&redirect, redirect_limit - 1)
+ } else {
+ Some(specifier.clone())
+ }
+ } else {
+ None
+ }
+ }
+}
diff --git a/cli/lsp/tsc.rs b/cli/lsp/tsc.rs
index 61308092e..bed71f6d9 100644
--- a/cli/lsp/tsc.rs
+++ b/cli/lsp/tsc.rs
@@ -4130,9 +4130,9 @@ fn op_script_names(state: &mut OpState) -> Vec<String> {
}
// inject these next because they're global
- for import in documents.module_graph_imports() {
- if seen.insert(import.as_str()) {
- result.push(import.to_string());
+ for specifier in state.state_snapshot.resolver.graph_import_specifiers() {
+ if seen.insert(specifier.as_str()) {
+ result.push(specifier.to_string());
}
}
@@ -5110,7 +5110,7 @@ mod tests {
)
.await;
let resolver = LspResolver::default()
- .with_new_config(&config, None, None)
+ .with_new_config(&config, cache.clone(), None, None)
.await;
StateSnapshot {
project_version: 0,