node_resolver/
package_json.rs1use std::cell::RefCell;
4use std::collections::HashMap;
5use std::io::ErrorKind;
6use std::path::Path;
7use std::path::PathBuf;
8
9use deno_package_json::PackageJson;
10use deno_package_json::PackageJsonRc;
11use sys_traits::FsRead;
12
13use crate::errors::ClosestPkgJsonError;
14use crate::errors::PackageJsonLoadError;
15
16pub trait NodePackageJsonCache:
17 deno_package_json::PackageJsonCache
18 + std::fmt::Debug
19 + crate::sync::MaybeSend
20 + crate::sync::MaybeSync
21{
22 fn as_deno_package_json_cache(
23 &self,
24 ) -> &dyn deno_package_json::PackageJsonCache;
25}
26
27impl<T> NodePackageJsonCache for T
28where
29 T: deno_package_json::PackageJsonCache
30 + std::fmt::Debug
31 + crate::sync::MaybeSend
32 + crate::sync::MaybeSync,
33{
34 fn as_deno_package_json_cache(
35 &self,
36 ) -> &dyn deno_package_json::PackageJsonCache {
37 self
38 }
39}
40
41#[allow(clippy::disallowed_types)]
42pub type PackageJsonCacheRc = crate::sync::MaybeArc<dyn NodePackageJsonCache>;
43
44thread_local! {
45 static CACHE: RefCell<HashMap<PathBuf, PackageJsonRc>> = RefCell::new(HashMap::new());
46}
47
48#[derive(Debug)]
49pub struct PackageJsonThreadLocalCache;
50
51impl PackageJsonThreadLocalCache {
52 pub fn clear() {
53 CACHE.with_borrow_mut(|cache| cache.clear());
54 }
55}
56
57impl deno_package_json::PackageJsonCache for PackageJsonThreadLocalCache {
58 fn get(&self, path: &Path) -> Option<PackageJsonRc> {
59 CACHE.with_borrow(|cache| cache.get(path).cloned())
60 }
61
62 fn set(&self, path: PathBuf, package_json: PackageJsonRc) {
63 CACHE.with_borrow_mut(|cache| cache.insert(path, package_json));
64 }
65}
66
67#[allow(clippy::disallowed_types)]
68pub type PackageJsonResolverRc<TSys> =
69 crate::sync::MaybeArc<PackageJsonResolver<TSys>>;
70
71#[derive(Debug)]
72pub struct PackageJsonResolver<TSys: FsRead> {
73 sys: TSys,
74 loader_cache: Option<PackageJsonCacheRc>,
75}
76
77impl<TSys: FsRead> PackageJsonResolver<TSys> {
78 pub fn new(sys: TSys, loader_cache: Option<PackageJsonCacheRc>) -> Self {
79 Self { sys, loader_cache }
80 }
81
82 pub fn get_closest_package_json(
83 &self,
84 file_path: &Path,
85 ) -> Result<Option<PackageJsonRc>, ClosestPkgJsonError> {
86 let Some(parent_dir) = file_path.parent() else {
87 return Ok(None);
88 };
89 for current_dir in parent_dir.ancestors() {
90 let package_json_path = current_dir.join("package.json");
91 if let Some(pkg_json) = self.load_package_json(&package_json_path)? {
92 return Ok(Some(pkg_json));
93 }
94 }
95
96 Ok(None)
97 }
98
99 pub fn load_package_json(
100 &self,
101 path: &Path,
102 ) -> Result<Option<PackageJsonRc>, PackageJsonLoadError> {
103 let result = PackageJson::load_from_path(
104 &self.sys,
105 self
106 .loader_cache
107 .as_deref()
108 .map(|cache| cache.as_deno_package_json_cache()),
109 path,
110 );
111 match result {
112 Ok(pkg_json) => Ok(Some(pkg_json)),
113 Err(deno_package_json::PackageJsonLoadError::Io { source, .. })
114 if source.kind() == ErrorKind::NotFound =>
115 {
116 Ok(None)
117 }
118 Err(err) => Err(PackageJsonLoadError::PackageJson(err)),
119 }
120 }
121}