2024-01-01 14:58:21 -05:00
|
|
|
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
2022-08-20 11:31:33 -04:00
|
|
|
|
2024-07-23 17:34:46 -04:00
|
|
|
use deno_package_json::PackageJson;
|
|
|
|
use deno_package_json::PackageJsonRc;
|
2024-11-01 12:27:00 -04:00
|
|
|
use deno_path_util::strip_unc_prefix;
|
2023-02-06 10:20:20 -05:00
|
|
|
use std::cell::RefCell;
|
|
|
|
use std::collections::HashMap;
|
2022-08-20 11:31:33 -04:00
|
|
|
use std::io::ErrorKind;
|
2024-06-26 17:24:10 -04:00
|
|
|
use std::path::Path;
|
2022-08-20 11:31:33 -04:00
|
|
|
use std::path::PathBuf;
|
2024-11-01 12:27:00 -04:00
|
|
|
use url::Url;
|
2022-08-20 11:31:33 -04:00
|
|
|
|
2024-11-01 12:27:00 -04:00
|
|
|
use crate::env::NodeResolverEnv;
|
|
|
|
use crate::errors::CanonicalizingPkgJsonDirError;
|
|
|
|
use crate::errors::ClosestPkgJsonError;
|
2024-07-16 18:32:41 -04:00
|
|
|
use crate::errors::PackageJsonLoadError;
|
|
|
|
|
2024-11-13 10:10:09 -05:00
|
|
|
// 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
|
2023-02-06 10:20:20 -05:00
|
|
|
thread_local! {
|
2024-06-26 17:24:10 -04:00
|
|
|
static CACHE: RefCell<HashMap<PathBuf, PackageJsonRc>> = RefCell::new(HashMap::new());
|
2023-02-06 10:20:20 -05:00
|
|
|
}
|
|
|
|
|
2024-06-26 17:24:10 -04:00
|
|
|
pub struct PackageJsonThreadLocalCache;
|
2023-02-23 10:58:10 -05:00
|
|
|
|
2024-06-26 17:24:10 -04:00
|
|
|
impl PackageJsonThreadLocalCache {
|
|
|
|
pub fn clear() {
|
|
|
|
CACHE.with(|cache| cache.borrow_mut().clear());
|
2023-02-23 10:58:10 -05:00
|
|
|
}
|
2022-08-20 11:31:33 -04:00
|
|
|
}
|
|
|
|
|
2024-07-23 17:34:46 -04:00
|
|
|
impl deno_package_json::PackageJsonCache for PackageJsonThreadLocalCache {
|
2024-06-26 17:24:10 -04:00
|
|
|
fn get(&self, path: &Path) -> Option<PackageJsonRc> {
|
|
|
|
CACHE.with(|cache| cache.borrow().get(path).cloned())
|
2022-08-20 11:31:33 -04:00
|
|
|
}
|
|
|
|
|
2024-06-26 17:24:10 -04:00
|
|
|
fn set(&self, path: PathBuf, package_json: PackageJsonRc) {
|
|
|
|
CACHE.with(|cache| cache.borrow_mut().insert(path, package_json));
|
2022-08-20 11:31:33 -04:00
|
|
|
}
|
|
|
|
}
|
2023-09-22 05:21:38 -04:00
|
|
|
|
2024-11-01 12:27:00 -04:00
|
|
|
#[allow(clippy::disallowed_types)]
|
|
|
|
pub type PackageJsonResolverRc<TEnv> =
|
|
|
|
crate::sync::MaybeArc<PackageJsonResolver<TEnv>>;
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct PackageJsonResolver<TEnv: NodeResolverEnv> {
|
|
|
|
env: TEnv,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<TEnv: NodeResolverEnv> PackageJsonResolver<TEnv> {
|
|
|
|
pub fn new(env: TEnv) -> Self {
|
|
|
|
Self { env }
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn get_closest_package_json(
|
|
|
|
&self,
|
|
|
|
url: &Url,
|
|
|
|
) -> Result<Option<PackageJsonRc>, ClosestPkgJsonError> {
|
|
|
|
let Ok(file_path) = deno_path_util::url_to_file_path(url) else {
|
|
|
|
return Ok(None);
|
|
|
|
};
|
|
|
|
self.get_closest_package_json_from_path(&file_path)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn get_closest_package_json_from_path(
|
|
|
|
&self,
|
|
|
|
file_path: &Path,
|
|
|
|
) -> Result<Option<PackageJsonRc>, ClosestPkgJsonError> {
|
|
|
|
// we use this for deno compile using byonm because the script paths
|
|
|
|
// won't be in virtual file system, but the package.json paths will be
|
|
|
|
fn canonicalize_first_ancestor_exists<TEnv: NodeResolverEnv>(
|
|
|
|
dir_path: &Path,
|
|
|
|
env: &TEnv,
|
|
|
|
) -> Result<Option<PathBuf>, std::io::Error> {
|
|
|
|
for ancestor in dir_path.ancestors() {
|
|
|
|
match env.realpath_sync(ancestor) {
|
|
|
|
Ok(dir_path) => return Ok(Some(dir_path)),
|
|
|
|
Err(err) if err.kind() == std::io::ErrorKind::NotFound => {
|
|
|
|
// keep searching
|
|
|
|
}
|
|
|
|
Err(err) => return Err(err),
|
|
|
|
}
|
|
|
|
}
|
2024-07-23 17:34:46 -04:00
|
|
|
Ok(None)
|
|
|
|
}
|
2024-11-01 12:27:00 -04:00
|
|
|
|
|
|
|
let parent_dir = file_path.parent().unwrap();
|
|
|
|
let Some(start_dir) = canonicalize_first_ancestor_exists(
|
|
|
|
parent_dir, &self.env,
|
|
|
|
)
|
|
|
|
.map_err(|source| CanonicalizingPkgJsonDirError {
|
|
|
|
dir_path: parent_dir.to_path_buf(),
|
|
|
|
source,
|
|
|
|
})?
|
|
|
|
else {
|
|
|
|
return Ok(None);
|
|
|
|
};
|
|
|
|
let start_dir = strip_unc_prefix(start_dir);
|
|
|
|
for current_dir in start_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<Option<PackageJsonRc>, 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)),
|
|
|
|
}
|
2023-09-22 05:21:38 -04:00
|
|
|
}
|
|
|
|
}
|