// Copyright 2018-2025 the Deno authors. MIT license. use std::cell::RefCell; use std::collections::HashMap; use std::io::ErrorKind; use std::path::Path; use std::path::PathBuf; use deno_package_json::PackageJson; use deno_package_json::PackageJsonRc; use sys_traits::FsRead; use crate::errors::PackageJsonLoadError; pub trait NodePackageJsonCache: deno_package_json::PackageJsonCache + std::fmt::Debug + deno_maybe_sync::MaybeSend + deno_maybe_sync::MaybeSync { fn as_deno_package_json_cache( &self, ) -> &dyn deno_package_json::PackageJsonCache; } impl NodePackageJsonCache for T where T: deno_package_json::PackageJsonCache + std::fmt::Debug + deno_maybe_sync::MaybeSend + deno_maybe_sync::MaybeSync, { fn as_deno_package_json_cache( &self, ) -> &dyn deno_package_json::PackageJsonCache { self } } #[allow(clippy::disallowed_types)] pub type PackageJsonCacheRc = deno_maybe_sync::MaybeArc; thread_local! { static CACHE: RefCell> = RefCell::new(HashMap::new()); } #[derive(Debug)] pub struct PackageJsonThreadLocalCache; impl PackageJsonThreadLocalCache { pub fn clear() { CACHE.with_borrow_mut(|cache| cache.clear()); } } impl deno_package_json::PackageJsonCache for PackageJsonThreadLocalCache { fn get(&self, path: &Path) -> Option { CACHE.with_borrow(|cache| cache.get(path).cloned()) } fn set(&self, path: PathBuf, package_json: PackageJsonRc) { CACHE.with_borrow_mut(|cache| cache.insert(path, package_json)); } } #[allow(clippy::disallowed_types)] pub type PackageJsonResolverRc = deno_maybe_sync::MaybeArc>; #[derive(Debug)] pub struct PackageJsonResolver { sys: TSys, loader_cache: Option, } impl PackageJsonResolver { pub fn new(sys: TSys, loader_cache: Option) -> Self { Self { sys, loader_cache } } pub fn get_closest_package_json( &self, file_path: &Path, ) -> Result, PackageJsonLoadError> { self.get_closest_package_jsons(file_path).next().transpose() } /// Gets the closest package.json files, iterating from the /// nearest directory to the furthest ancestor directory. pub fn get_closest_package_jsons<'a>( &'a self, file_path: &'a Path, ) -> ClosestPackageJsonsIterator<'a, TSys> { ClosestPackageJsonsIterator { current_path: file_path, resolver: self, } } pub fn load_package_json( &self, path: &Path, ) -> Result, PackageJsonLoadError> { let result = PackageJson::load_from_path( &self.sys, self .loader_cache .as_deref() .map(|cache| cache.as_deno_package_json_cache()), path, ); 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)), } } } pub struct ClosestPackageJsonsIterator<'a, TSys: FsRead> { current_path: &'a Path, resolver: &'a PackageJsonResolver, } impl<'a, TSys: FsRead> Iterator for ClosestPackageJsonsIterator<'a, TSys> { type Item = Result; fn next(&mut self) -> Option { while let Some(parent) = self.current_path.parent() { self.current_path = parent; let package_json_path = parent.join("package.json"); match self.resolver.load_package_json(&package_json_path) { Ok(Some(value)) => return Some(Ok(value)), Ok(None) => { // skip } Err(err) => return Some(Err(err)), } } None } }