diff options
author | Nayeem Rahman <nayeemrmn99@gmail.com> | 2024-06-10 17:03:17 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-06-10 17:03:17 +0100 |
commit | 7c5dbd5d54770dba5e56442b633e9597403ef5da (patch) | |
tree | 3837f975b8d6a6615c91fd5c1e37ac2732a8aaf5 /cli/jsr.rs | |
parent | 4fd3d5a86e45c4dcbaaa277cfb7f1087ddebfa48 (diff) |
feat(lsp): workspace jsr resolution (#24121)
Diffstat (limited to 'cli/jsr.rs')
-rw-r--r-- | cli/jsr.rs | 219 |
1 files changed, 1 insertions, 218 deletions
diff --git a/cli/jsr.rs b/cli/jsr.rs index e582ab9f0..87a54af22 100644 --- a/cli/jsr.rs +++ b/cli/jsr.rs @@ -3,207 +3,14 @@ use crate::args::jsr_url; use crate::file_fetcher::FileFetcher; use dashmap::DashMap; -use deno_cache_dir::HttpCache; -use deno_core::parking_lot::Mutex; use deno_core::serde_json; -use deno_core::ModuleSpecifier; use deno_graph::packages::JsrPackageInfo; use deno_graph::packages::JsrPackageVersionInfo; -use deno_lockfile::Lockfile; use deno_runtime::deno_permissions::PermissionsContainer; -use deno_semver::jsr::JsrPackageReqReference; use deno_semver::package::PackageNv; use deno_semver::package::PackageReq; -use std::borrow::Cow; use std::sync::Arc; -/// Keep in sync with `JsrFetchResolver`! -#[derive(Debug)] -pub struct JsrCacheResolver { - nv_by_req: DashMap<PackageReq, Option<PackageNv>>, - /// The `module_graph` field of the version infos should be forcibly absent. - /// It can be large and we don't want to store it. - info_by_nv: DashMap<PackageNv, Option<Arc<JsrPackageVersionInfo>>>, - info_by_name: DashMap<String, Option<Arc<JsrPackageInfo>>>, - cache: Arc<dyn HttpCache>, -} - -impl JsrCacheResolver { - pub fn new( - cache: Arc<dyn HttpCache>, - lockfile: Option<Arc<Mutex<Lockfile>>>, - ) -> Self { - let nv_by_req = DashMap::new(); - if let Some(lockfile) = lockfile { - for (req_url, nv_url) in &lockfile.lock().content.packages.specifiers { - let Some(req) = req_url.strip_prefix("jsr:") else { - continue; - }; - let Some(nv) = nv_url.strip_prefix("jsr:") else { - continue; - }; - let Ok(req) = PackageReq::from_str(req) else { - continue; - }; - let Ok(nv) = PackageNv::from_str(nv) else { - continue; - }; - nv_by_req.insert(req, Some(nv)); - } - } - Self { - nv_by_req, - info_by_nv: Default::default(), - info_by_name: Default::default(), - cache: cache.clone(), - } - } - - pub fn req_to_nv(&self, req: &PackageReq) -> Option<PackageNv> { - if let Some(nv) = self.nv_by_req.get(req) { - return nv.value().clone(); - } - let maybe_get_nv = || { - let name = req.name.clone(); - let package_info = self.package_info(&name)?; - // Find the first matching version of the package which is cached. - let mut versions = package_info.versions.keys().collect::<Vec<_>>(); - versions.sort(); - let version = versions - .into_iter() - .rev() - .find(|v| { - if req.version_req.tag().is_some() || !req.version_req.matches(v) { - return false; - } - let nv = PackageNv { - name: name.clone(), - version: (*v).clone(), - }; - self.package_version_info(&nv).is_some() - }) - .cloned()?; - Some(PackageNv { name, version }) - }; - let nv = maybe_get_nv(); - self.nv_by_req.insert(req.clone(), nv.clone()); - nv - } - - pub fn jsr_to_registry_url( - &self, - req_ref: &JsrPackageReqReference, - ) -> Option<ModuleSpecifier> { - let req = req_ref.req().clone(); - let maybe_nv = self.req_to_nv(&req); - let nv = maybe_nv.as_ref()?; - let info = self.package_version_info(nv)?; - let path = info.export(&normalize_export_name(req_ref.sub_path()))?; - jsr_url() - .join(&format!("{}/{}/{}", &nv.name, &nv.version, &path)) - .ok() - } - - pub fn lookup_export_for_path( - &self, - nv: &PackageNv, - path: &str, - ) -> Option<String> { - let info = self.package_version_info(nv)?; - let path = path.strip_prefix("./").unwrap_or(path); - let mut sloppy_fallback = None; - for (export, path_) in info.exports() { - let path_ = path_.strip_prefix("./").unwrap_or(path_); - if path_ == path { - return Some(export.strip_prefix("./").unwrap_or(export).to_string()); - } - // TSC in some cases will suggest a `.js` import path for a `.d.ts` source - // file. - if sloppy_fallback.is_none() { - let path = path - .strip_suffix(".js") - .or_else(|| path.strip_suffix(".mjs")) - .or_else(|| path.strip_suffix(".cjs")) - .unwrap_or(path); - let path_ = path_ - .strip_suffix(".d.ts") - .or_else(|| path_.strip_suffix(".d.mts")) - .or_else(|| path_.strip_suffix(".d.cts")) - .unwrap_or(path_); - if path_ == path { - sloppy_fallback = - Some(export.strip_prefix("./").unwrap_or(export).to_string()); - } - } - } - sloppy_fallback - } - - pub fn lookup_req_for_nv(&self, nv: &PackageNv) -> Option<PackageReq> { - for entry in self.nv_by_req.iter() { - let Some(nv_) = entry.value() else { - continue; - }; - if nv_ == nv { - return Some(entry.key().clone()); - } - } - None - } - - pub fn package_info(&self, name: &str) -> Option<Arc<JsrPackageInfo>> { - if let Some(info) = self.info_by_name.get(name) { - return info.value().clone(); - } - let read_cached_package_info = || { - let meta_url = jsr_url().join(&format!("{}/meta.json", name)).ok()?; - let meta_bytes = read_cached_url(&meta_url, &self.cache)?; - serde_json::from_slice::<JsrPackageInfo>(&meta_bytes).ok() - }; - let info = read_cached_package_info().map(Arc::new); - self.info_by_name.insert(name.to_string(), info.clone()); - info - } - - pub fn package_version_info( - &self, - nv: &PackageNv, - ) -> Option<Arc<JsrPackageVersionInfo>> { - if let Some(info) = self.info_by_nv.get(nv) { - return info.value().clone(); - } - let read_cached_package_version_info = || { - let meta_url = jsr_url() - .join(&format!("{}/{}_meta.json", &nv.name, &nv.version)) - .ok()?; - let meta_bytes = read_cached_url(&meta_url, &self.cache)?; - partial_jsr_package_version_info_from_slice(&meta_bytes).ok() - }; - let info = read_cached_package_version_info().map(Arc::new); - 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( - url: &ModuleSpecifier, - cache: &Arc<dyn HttpCache>, -) -> Option<Vec<u8>> { - cache - .read_file_bytes( - &cache.cache_item_key(url).ok()?, - None, - deno_cache_dir::GlobalToLocalCopy::Disallow, - ) - .ok()? -} - /// This is similar to a subset of `JsrCacheResolver` which fetches rather than /// just reads the cache. Keep in sync! #[derive(Debug)] @@ -304,33 +111,9 @@ impl JsrFetchResolver { } } -// TODO(nayeemrmn): This is duplicated from a private function in deno_graph -// 0.65.1. Make it public or cleanup otherwise. -fn normalize_export_name(sub_path: Option<&str>) -> Cow<str> { - let Some(sub_path) = sub_path else { - return Cow::Borrowed("."); - }; - if sub_path.is_empty() || matches!(sub_path, "/" | ".") { - Cow::Borrowed(".") - } else { - let sub_path = if sub_path.starts_with('/') { - Cow::Owned(format!(".{}", sub_path)) - } else if !sub_path.starts_with("./") { - Cow::Owned(format!("./{}", sub_path)) - } else { - Cow::Borrowed(sub_path) - }; - if let Some(prefix) = sub_path.strip_suffix('/') { - Cow::Owned(prefix.to_string()) - } else { - sub_path - } - } -} - /// This is a roundabout way of deserializing `JsrPackageVersionInfo`, /// because we only want the `exports` field and `module_graph` is large. -fn partial_jsr_package_version_info_from_slice( +pub fn partial_jsr_package_version_info_from_slice( slice: &[u8], ) -> serde_json::Result<JsrPackageVersionInfo> { let mut info = serde_json::from_slice::<serde_json::Value>(slice)?; |