mirror of
https://github.com/denoland/deno.git
synced 2024-11-24 15:19:26 -05:00
refactor(lsp): move fields from Documents to LspResolver (#23585)
This commit is contained in:
parent
783533d2e3
commit
5cae343991
6 changed files with 203 additions and 180 deletions
|
@ -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()
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,7 +31,6 @@ 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;
|
||||
|
@ -716,64 +714,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>>,
|
||||
|
@ -918,21 +858,15 @@ 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,
|
||||
}
|
||||
|
@ -945,29 +879,14 @@ 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
|
||||
|
@ -1102,11 +1021,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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1279,7 +1201,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));
|
||||
|
@ -1308,62 +1231,19 @@ 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);
|
||||
|
@ -1516,19 +1396,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(
|
||||
|
@ -1702,20 +1569,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);
|
||||
|
@ -1741,7 +1608,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);
|
||||
|
@ -1785,7 +1652,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();
|
||||
|
@ -1813,7 +1680,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");
|
||||
|
@ -1862,9 +1729,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(
|
||||
|
@ -1906,9 +1778,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();
|
||||
|
|
|
@ -312,9 +312,8 @@ impl LanguageServer {
|
|||
.show_message(MessageType::WARNING, err);
|
||||
}
|
||||
let mut inner = self.0.write().await;
|
||||
let lockfile = inner.config.tree.root_lockfile().cloned();
|
||||
inner.documents.refresh_lockfile(lockfile);
|
||||
inner.refresh_npm_specifiers().await;
|
||||
inner.refresh_resolver().await;
|
||||
inner.refresh_documents_config().await;
|
||||
inner.post_cache(result.mark).await;
|
||||
}
|
||||
Ok(Some(json!(true)))
|
||||
|
@ -687,7 +686,6 @@ impl Inner {
|
|||
.map(|c| c as Arc<dyn HttpCache>)
|
||||
.unwrap_or(global_cache);
|
||||
self.deps_http_cache = cache.clone();
|
||||
self.documents.set_cache(cache.clone());
|
||||
self.cache_metadata.set_cache(cache);
|
||||
self.url_map.set_cache(maybe_local_cache);
|
||||
self.maybe_global_cache_path = new_cache_path;
|
||||
|
@ -810,8 +808,6 @@ impl Inner {
|
|||
self.config.update_capabilities(¶ms.capabilities);
|
||||
}
|
||||
|
||||
self.documents.initialize(&self.config);
|
||||
|
||||
if let Err(e) = self
|
||||
.ts_server
|
||||
.start(self.config.internal_inspect().to_address())
|
||||
|
@ -1016,10 +1012,14 @@ impl Inner {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn refresh_resolver(&mut self) {
|
||||
self.resolver = self
|
||||
.resolver
|
||||
.with_new_config(
|
||||
&self.config,
|
||||
self.deps_http_cache.clone(),
|
||||
self.maybe_global_cache_path.as_deref(),
|
||||
Some(&self.http_client),
|
||||
)
|
||||
|
@ -1030,6 +1030,7 @@ impl Inner {
|
|||
self.documents.update_config(
|
||||
&self.config,
|
||||
&self.resolver,
|
||||
self.deps_http_cache.clone(),
|
||||
&self.workspace_files,
|
||||
);
|
||||
|
||||
|
@ -1169,6 +1170,7 @@ impl Inner {
|
|||
lsp_warn!("Error updating registries: {:#}", err);
|
||||
self.client.show_message(MessageType::WARNING, err);
|
||||
}
|
||||
self.refresh_resolver().await;
|
||||
self.refresh_documents_config().await;
|
||||
self.diagnostics_server.invalidate_all();
|
||||
self.send_diagnostics_update();
|
||||
|
@ -1217,6 +1219,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 {
|
||||
|
@ -1518,10 +1521,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}>)");
|
||||
}
|
||||
|
@ -2991,6 +2991,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();
|
||||
|
@ -3125,6 +3126,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();
|
||||
|
|
|
@ -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,15 +149,18 @@ 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(),
|
||||
})
|
||||
}
|
||||
|
@ -136,10 +185,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 +291,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 +373,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 +393,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 +423,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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4124,9 +4124,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());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5095,7 +5095,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,
|
||||
|
|
Loading…
Reference in a new issue