node_resolver/
package_json.rs

1// Copyright 2018-2025 the Deno authors. MIT license.
2
3use 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}