// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. use deno_package_json::PackageJson; use deno_package_json::PackageJsonRc; use std::cell::RefCell; use std::collections::HashMap; use std::io::ErrorKind; use std::path::Path; use std::path::PathBuf; use url::Url; use crate::env::NodeResolverEnv; use crate::errors::ClosestPkgJsonError; use crate::errors::PackageJsonLoadError; // it would be nice if this was passed down as a ctor arg to the package.json resolver, // but it's a little bit complicated to do that, so we just maintain a thread local cache thread_local! { static CACHE: RefCell> = RefCell::new(HashMap::new()); } pub struct PackageJsonThreadLocalCache; impl PackageJsonThreadLocalCache { pub fn clear() { CACHE.with(|cache| cache.borrow_mut().clear()); } } impl deno_package_json::PackageJsonCache for PackageJsonThreadLocalCache { fn get(&self, path: &Path) -> Option { CACHE.with(|cache| cache.borrow().get(path).cloned()) } fn set(&self, path: PathBuf, package_json: PackageJsonRc) { CACHE.with(|cache| cache.borrow_mut().insert(path, package_json)); } } #[allow(clippy::disallowed_types)] pub type PackageJsonResolverRc = crate::sync::MaybeArc>; #[derive(Debug)] pub struct PackageJsonResolver { env: TEnv, } impl PackageJsonResolver { pub fn new(env: TEnv) -> Self { Self { env } } pub fn get_closest_package_json( &self, url: &Url, ) -> Result, ClosestPkgJsonError> { let Ok(file_path) = deno_path_util::url_to_file_path(url) else { return Ok(None); }; self.get_closest_package_json_from_file_path(&file_path) } pub fn get_closest_package_json_from_file_path( &self, file_path: &Path, ) -> Result, ClosestPkgJsonError> { let parent_dir = file_path.parent().unwrap(); for current_dir in parent_dir.ancestors() { let package_json_path = current_dir.join("package.json"); if let Some(pkg_json) = self.load_package_json(&package_json_path)? { return Ok(Some(pkg_json)); } } Ok(None) } pub fn load_package_json( &self, path: &Path, ) -> Result, PackageJsonLoadError> { let result = PackageJson::load_from_path( path, self.env.pkg_json_fs(), Some(&PackageJsonThreadLocalCache), ); match result { Ok(pkg_json) => Ok(Some(pkg_json)), Err(deno_package_json::PackageJsonLoadError::Io { source, .. }) if source.kind() == ErrorKind::NotFound => { Ok(None) } Err(err) => Err(PackageJsonLoadError(err)), } } }