summaryrefslogtreecommitdiff
path: root/resolvers/deno/npm
diff options
context:
space:
mode:
authorhaturau <135221985+haturatu@users.noreply.github.com>2024-11-20 01:20:47 +0900
committerGitHub <noreply@github.com>2024-11-20 01:20:47 +0900
commit85719a67e59c7aa45bead26e4942d7df8b1b42d4 (patch)
treeface0aecaac53e93ce2f23b53c48859bcf1a36ec /resolvers/deno/npm
parent67697bc2e4a62a9670699fd18ad0dd8efc5bd955 (diff)
parent186b52731c6bb326c4d32905c5e732d082e83465 (diff)
Merge branch 'denoland:main' into main
Diffstat (limited to 'resolvers/deno/npm')
-rw-r--r--resolvers/deno/npm/byonm.rs62
-rw-r--r--resolvers/deno/npm/mod.rs269
2 files changed, 316 insertions, 15 deletions
diff --git a/resolvers/deno/npm/byonm.rs b/resolvers/deno/npm/byonm.rs
index 3394b3e50..e9182d47a 100644
--- a/resolvers/deno/npm/byonm.rs
+++ b/resolvers/deno/npm/byonm.rs
@@ -10,19 +10,22 @@ use deno_package_json::PackageJsonDepValue;
use deno_path_util::url_to_file_path;
use deno_semver::package::PackageReq;
use deno_semver::Version;
+use node_resolver::env::NodeResolverEnv;
use node_resolver::errors::PackageFolderResolveError;
use node_resolver::errors::PackageFolderResolveIoError;
use node_resolver::errors::PackageJsonLoadError;
use node_resolver::errors::PackageNotFoundError;
-use node_resolver::load_pkg_json;
-use node_resolver::NpmResolver;
+use node_resolver::InNpmPackageChecker;
+use node_resolver::NpmPackageFolderResolver;
+use node_resolver::PackageJsonResolverRc;
use thiserror::Error;
use url::Url;
-use crate::fs::DenoPkgJsonFsAdapter;
use crate::fs::DenoResolverFs;
use super::local::normalize_pkg_name_for_node_modules_deno_folder;
+use super::CliNpmReqResolver;
+use super::ResolvePkgFolderFromDenoReqError;
#[derive(Debug, Error)]
pub enum ByonmResolvePkgFolderFromDenoReqError {
@@ -36,32 +39,41 @@ pub enum ByonmResolvePkgFolderFromDenoReqError {
Io(#[from] std::io::Error),
}
-pub struct ByonmNpmResolverCreateOptions<Fs: DenoResolverFs> {
- pub fs: Fs,
+pub struct ByonmNpmResolverCreateOptions<
+ Fs: DenoResolverFs,
+ TEnv: NodeResolverEnv,
+> {
// todo(dsherret): investigate removing this
pub root_node_modules_dir: Option<PathBuf>,
+ pub fs: Fs,
+ pub pkg_json_resolver: PackageJsonResolverRc<TEnv>,
}
#[derive(Debug)]
-pub struct ByonmNpmResolver<Fs: DenoResolverFs> {
+pub struct ByonmNpmResolver<Fs: DenoResolverFs, TEnv: NodeResolverEnv> {
fs: Fs,
+ pkg_json_resolver: PackageJsonResolverRc<TEnv>,
root_node_modules_dir: Option<PathBuf>,
}
-impl<Fs: DenoResolverFs + Clone> Clone for ByonmNpmResolver<Fs> {
+impl<Fs: DenoResolverFs + Clone, TEnv: NodeResolverEnv> Clone
+ for ByonmNpmResolver<Fs, TEnv>
+{
fn clone(&self) -> Self {
Self {
fs: self.fs.clone(),
+ pkg_json_resolver: self.pkg_json_resolver.clone(),
root_node_modules_dir: self.root_node_modules_dir.clone(),
}
}
}
-impl<Fs: DenoResolverFs> ByonmNpmResolver<Fs> {
- pub fn new(options: ByonmNpmResolverCreateOptions<Fs>) -> Self {
+impl<Fs: DenoResolverFs, TEnv: NodeResolverEnv> ByonmNpmResolver<Fs, TEnv> {
+ pub fn new(options: ByonmNpmResolverCreateOptions<Fs, TEnv>) -> Self {
Self {
- fs: options.fs,
root_node_modules_dir: options.root_node_modules_dir,
+ fs: options.fs,
+ pkg_json_resolver: options.pkg_json_resolver,
}
}
@@ -73,7 +85,7 @@ impl<Fs: DenoResolverFs> ByonmNpmResolver<Fs> {
&self,
path: &Path,
) -> Result<Option<Arc<PackageJson>>, PackageJsonLoadError> {
- load_pkg_json(&DenoPkgJsonFsAdapter(&self.fs), path)
+ self.pkg_json_resolver.load_package_json(path)
}
/// Finds the ancestor package.json that contains the specified dependency.
@@ -290,8 +302,27 @@ impl<Fs: DenoResolverFs> ByonmNpmResolver<Fs> {
}
}
-impl<Fs: DenoResolverFs + Send + Sync + std::fmt::Debug> NpmResolver
- for ByonmNpmResolver<Fs>
+impl<
+ Fs: DenoResolverFs + Send + Sync + std::fmt::Debug,
+ TEnv: NodeResolverEnv,
+ > CliNpmReqResolver for ByonmNpmResolver<Fs, TEnv>
+{
+ fn resolve_pkg_folder_from_deno_module_req(
+ &self,
+ req: &PackageReq,
+ referrer: &Url,
+ ) -> Result<PathBuf, ResolvePkgFolderFromDenoReqError> {
+ ByonmNpmResolver::resolve_pkg_folder_from_deno_module_req(
+ self, req, referrer,
+ )
+ .map_err(ResolvePkgFolderFromDenoReqError::Byonm)
+ }
+}
+
+impl<
+ Fs: DenoResolverFs + Send + Sync + std::fmt::Debug,
+ TEnv: NodeResolverEnv,
+ > NpmPackageFolderResolver for ByonmNpmResolver<Fs, TEnv>
{
fn resolve_package_folder_from_package(
&self,
@@ -342,7 +373,12 @@ impl<Fs: DenoResolverFs + Send + Sync + std::fmt::Debug> NpmResolver
.into()
})
}
+}
+
+#[derive(Debug)]
+pub struct ByonmInNpmPackageChecker;
+impl InNpmPackageChecker for ByonmInNpmPackageChecker {
fn in_npm_package(&self, specifier: &Url) -> bool {
specifier.scheme() == "file"
&& specifier
diff --git a/resolvers/deno/npm/mod.rs b/resolvers/deno/npm/mod.rs
index 9d885cad3..09e35b15c 100644
--- a/resolvers/deno/npm/mod.rs
+++ b/resolvers/deno/npm/mod.rs
@@ -1,9 +1,274 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
-mod byonm;
-mod local;
+use std::fmt::Debug;
+use std::path::PathBuf;
+use std::sync::Arc;
+
+use boxed_error::Boxed;
+use deno_semver::npm::NpmPackageReqReference;
+use deno_semver::package::PackageReq;
+use node_resolver::env::NodeResolverEnv;
+use node_resolver::errors::NodeResolveError;
+use node_resolver::errors::NodeResolveErrorKind;
+use node_resolver::errors::PackageFolderResolveErrorKind;
+use node_resolver::errors::PackageFolderResolveIoError;
+use node_resolver::errors::PackageNotFoundError;
+use node_resolver::errors::PackageResolveErrorKind;
+use node_resolver::errors::PackageSubpathResolveError;
+use node_resolver::InNpmPackageChecker;
+use node_resolver::NodeModuleKind;
+use node_resolver::NodeResolution;
+use node_resolver::NodeResolutionMode;
+use node_resolver::NodeResolver;
+use thiserror::Error;
+use url::Url;
+use crate::fs::DenoResolverFs;
+
+pub use byonm::ByonmInNpmPackageChecker;
pub use byonm::ByonmNpmResolver;
pub use byonm::ByonmNpmResolverCreateOptions;
pub use byonm::ByonmResolvePkgFolderFromDenoReqError;
pub use local::normalize_pkg_name_for_node_modules_deno_folder;
+
+mod byonm;
+mod local;
+
+#[derive(Debug, Error)]
+#[error("Could not resolve \"{}\", but found it in a package.json. Deno expects the node_modules/ directory to be up to date. Did you forget to run `deno install`?", specifier)]
+pub struct NodeModulesOutOfDateError {
+ pub specifier: String,
+}
+
+#[derive(Debug, Error)]
+#[error("Could not find '{}'. Deno expects the node_modules/ directory to be up to date. Did you forget to run `deno install`?", package_json_path.display())]
+pub struct MissingPackageNodeModulesFolderError {
+ pub package_json_path: PathBuf,
+}
+
+#[derive(Debug, Boxed)]
+pub struct ResolveIfForNpmPackageError(
+ pub Box<ResolveIfForNpmPackageErrorKind>,
+);
+
+#[derive(Debug, Error)]
+pub enum ResolveIfForNpmPackageErrorKind {
+ #[error(transparent)]
+ NodeResolve(#[from] NodeResolveError),
+ #[error(transparent)]
+ NodeModulesOutOfDate(#[from] NodeModulesOutOfDateError),
+}
+
+#[derive(Debug, Boxed)]
+pub struct ResolveReqWithSubPathError(pub Box<ResolveReqWithSubPathErrorKind>);
+
+#[derive(Debug, Error)]
+pub enum ResolveReqWithSubPathErrorKind {
+ #[error(transparent)]
+ MissingPackageNodeModulesFolder(#[from] MissingPackageNodeModulesFolderError),
+ #[error(transparent)]
+ ResolvePkgFolderFromDenoReq(#[from] ResolvePkgFolderFromDenoReqError),
+ #[error(transparent)]
+ PackageSubpathResolve(#[from] PackageSubpathResolveError),
+}
+
+#[derive(Debug, Error)]
+pub enum ResolvePkgFolderFromDenoReqError {
+ // todo(dsherret): don't use anyhow here
+ #[error(transparent)]
+ Managed(anyhow::Error),
+ #[error(transparent)]
+ Byonm(#[from] ByonmResolvePkgFolderFromDenoReqError),
+}
+
+// todo(dsherret): a temporary trait until we extract
+// out the CLI npm resolver into here
+pub trait CliNpmReqResolver: Debug + Send + Sync {
+ fn resolve_pkg_folder_from_deno_module_req(
+ &self,
+ req: &PackageReq,
+ referrer: &Url,
+ ) -> Result<PathBuf, ResolvePkgFolderFromDenoReqError>;
+}
+
+pub struct NpmReqResolverOptions<
+ Fs: DenoResolverFs,
+ TNodeResolverEnv: NodeResolverEnv,
+> {
+ /// The resolver when "bring your own node_modules" is enabled where Deno
+ /// does not setup the node_modules directories automatically, but instead
+ /// uses what already exists on the file system.
+ pub byonm_resolver: Option<Arc<ByonmNpmResolver<Fs, TNodeResolverEnv>>>,
+ pub fs: Fs,
+ pub in_npm_pkg_checker: Arc<dyn InNpmPackageChecker>,
+ pub node_resolver: Arc<NodeResolver<TNodeResolverEnv>>,
+ pub npm_req_resolver: Arc<dyn CliNpmReqResolver>,
+}
+
+#[derive(Debug)]
+pub struct NpmReqResolver<Fs: DenoResolverFs, TNodeResolverEnv: NodeResolverEnv>
+{
+ byonm_resolver: Option<Arc<ByonmNpmResolver<Fs, TNodeResolverEnv>>>,
+ fs: Fs,
+ in_npm_pkg_checker: Arc<dyn InNpmPackageChecker>,
+ node_resolver: Arc<NodeResolver<TNodeResolverEnv>>,
+ npm_resolver: Arc<dyn CliNpmReqResolver>,
+}
+
+impl<Fs: DenoResolverFs, TNodeResolverEnv: NodeResolverEnv>
+ NpmReqResolver<Fs, TNodeResolverEnv>
+{
+ pub fn new(options: NpmReqResolverOptions<Fs, TNodeResolverEnv>) -> Self {
+ Self {
+ byonm_resolver: options.byonm_resolver,
+ fs: options.fs,
+ in_npm_pkg_checker: options.in_npm_pkg_checker,
+ node_resolver: options.node_resolver,
+ npm_resolver: options.npm_req_resolver,
+ }
+ }
+
+ pub fn resolve_req_reference(
+ &self,
+ req_ref: &NpmPackageReqReference,
+ referrer: &Url,
+ referrer_kind: NodeModuleKind,
+ mode: NodeResolutionMode,
+ ) -> Result<Url, ResolveReqWithSubPathError> {
+ self.resolve_req_with_sub_path(
+ req_ref.req(),
+ req_ref.sub_path(),
+ referrer,
+ referrer_kind,
+ mode,
+ )
+ }
+
+ pub fn resolve_req_with_sub_path(
+ &self,
+ req: &PackageReq,
+ sub_path: Option<&str>,
+ referrer: &Url,
+ referrer_kind: NodeModuleKind,
+ mode: NodeResolutionMode,
+ ) -> Result<Url, ResolveReqWithSubPathError> {
+ let package_folder = self
+ .npm_resolver
+ .resolve_pkg_folder_from_deno_module_req(req, referrer)?;
+ let resolution_result =
+ self.node_resolver.resolve_package_subpath_from_deno_module(
+ &package_folder,
+ sub_path,
+ Some(referrer),
+ referrer_kind,
+ mode,
+ );
+ match resolution_result {
+ Ok(url) => Ok(url),
+ Err(err) => {
+ if self.byonm_resolver.is_some() {
+ let package_json_path = package_folder.join("package.json");
+ if !self.fs.exists_sync(&package_json_path) {
+ return Err(
+ MissingPackageNodeModulesFolderError { package_json_path }.into(),
+ );
+ }
+ }
+ Err(err.into())
+ }
+ }
+ }
+
+ pub fn resolve_if_for_npm_pkg(
+ &self,
+ specifier: &str,
+ referrer: &Url,
+ referrer_kind: NodeModuleKind,
+ mode: NodeResolutionMode,
+ ) -> Result<Option<NodeResolution>, ResolveIfForNpmPackageError> {
+ let resolution_result =
+ self
+ .node_resolver
+ .resolve(specifier, referrer, referrer_kind, mode);
+ match resolution_result {
+ Ok(res) => Ok(Some(res)),
+ Err(err) => {
+ let err = err.into_kind();
+ match err {
+ NodeResolveErrorKind::RelativeJoin(_)
+ | NodeResolveErrorKind::PackageImportsResolve(_)
+ | NodeResolveErrorKind::UnsupportedEsmUrlScheme(_)
+ | NodeResolveErrorKind::DataUrlReferrer(_)
+ | NodeResolveErrorKind::TypesNotFound(_)
+ | NodeResolveErrorKind::FinalizeResolution(_) => Err(
+ ResolveIfForNpmPackageErrorKind::NodeResolve(err.into()).into_box(),
+ ),
+ NodeResolveErrorKind::PackageResolve(err) => {
+ let err = err.into_kind();
+ match err {
+ PackageResolveErrorKind::ClosestPkgJson(_)
+ | PackageResolveErrorKind::InvalidModuleSpecifier(_)
+ | PackageResolveErrorKind::ExportsResolve(_)
+ | PackageResolveErrorKind::SubpathResolve(_) => Err(
+ ResolveIfForNpmPackageErrorKind::NodeResolve(
+ NodeResolveErrorKind::PackageResolve(err.into()).into(),
+ )
+ .into_box(),
+ ),
+ PackageResolveErrorKind::PackageFolderResolve(err) => {
+ match err.as_kind() {
+ PackageFolderResolveErrorKind::Io(
+ PackageFolderResolveIoError { package_name, .. },
+ )
+ | PackageFolderResolveErrorKind::PackageNotFound(
+ PackageNotFoundError { package_name, .. },
+ ) => {
+ if self.in_npm_pkg_checker.in_npm_package(referrer) {
+ return Err(
+ ResolveIfForNpmPackageErrorKind::NodeResolve(
+ NodeResolveErrorKind::PackageResolve(err.into())
+ .into(),
+ )
+ .into_box(),
+ );
+ }
+ if let Some(byonm_npm_resolver) = &self.byonm_resolver {
+ if byonm_npm_resolver
+ .find_ancestor_package_json_with_dep(
+ package_name,
+ referrer,
+ )
+ .is_some()
+ {
+ return Err(
+ ResolveIfForNpmPackageErrorKind::NodeModulesOutOfDate(
+ NodeModulesOutOfDateError {
+ specifier: specifier.to_string(),
+ },
+ ).into_box(),
+ );
+ }
+ }
+ Ok(None)
+ }
+ PackageFolderResolveErrorKind::ReferrerNotFound(_) => {
+ if self.in_npm_pkg_checker.in_npm_package(referrer) {
+ return Err(
+ ResolveIfForNpmPackageErrorKind::NodeResolve(
+ NodeResolveErrorKind::PackageResolve(err.into())
+ .into(),
+ )
+ .into_box(),
+ );
+ }
+ Ok(None)
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}