diff options
Diffstat (limited to 'cli/npm/byonm.rs')
-rw-r--r-- | cli/npm/byonm.rs | 143 |
1 files changed, 111 insertions, 32 deletions
diff --git a/cli/npm/byonm.rs b/cli/npm/byonm.rs index e62650d8a..3249b2ed1 100644 --- a/cli/npm/byonm.rs +++ b/cli/npm/byonm.rs @@ -17,6 +17,7 @@ use deno_runtime::deno_node::NodeRequireResolver; use deno_runtime::deno_node::NpmProcessStateProvider; use deno_runtime::deno_node::PackageJson; use deno_semver::package::PackageReq; +use deno_semver::Version; use node_resolver::errors::PackageFolderResolveError; use node_resolver::errors::PackageFolderResolveIoError; use node_resolver::errors::PackageJsonLoadError; @@ -29,6 +30,7 @@ use crate::args::NpmProcessStateKind; use crate::util::fs::canonicalize_path_maybe_not_exists_with_fs; use deno_runtime::fs_util::specifier_to_file_path; +use super::managed::normalize_pkg_name_for_node_modules_deno_folder; use super::CliNpmResolver; use super::InnerCliNpmResolverRef; @@ -60,9 +62,7 @@ impl ByonmCliNpmResolver { ) -> Result<Option<Arc<PackageJson>>, PackageJsonLoadError> { load_pkg_json(&DenoPkgJsonFsAdapter(self.fs.as_ref()), path) } -} -impl ByonmCliNpmResolver { /// Finds the ancestor package.json that contains the specified dependency. pub fn find_ancestor_package_json_with_dep( &self, @@ -98,7 +98,7 @@ impl ByonmCliNpmResolver { &self, req: &PackageReq, referrer: &ModuleSpecifier, - ) -> Result<(Arc<PackageJson>, String), AnyError> { + ) -> Result<Option<(Arc<PackageJson>, String)>, AnyError> { fn resolve_alias_from_pkg_json( req: &PackageReq, pkg_json: &PackageJson, @@ -134,7 +134,7 @@ impl ByonmCliNpmResolver { if let Some(alias) = resolve_alias_from_pkg_json(req, pkg_json.as_ref()) { - return Ok((pkg_json, alias)); + return Ok(Some((pkg_json, alias))); } } current_path = dir_path; @@ -148,19 +148,65 @@ impl ByonmCliNpmResolver { if let Some(pkg_json) = self.load_pkg_json(&root_pkg_json_path)? { if let Some(alias) = resolve_alias_from_pkg_json(req, pkg_json.as_ref()) { - return Ok((pkg_json, alias)); + return Ok(Some((pkg_json, alias))); } } } - bail!( - concat!( - "Could not find a matching package for 'npm:{}' in a package.json file. ", - "You must specify this as a package.json dependency when the ", - "node_modules folder is not managed by Deno.", - ), - req, + Ok(None) + } + + fn resolve_folder_in_root_node_modules( + &self, + req: &PackageReq, + ) -> Option<PathBuf> { + // now check if node_modules/.deno/ matches this constraint + let root_node_modules_dir = self.root_node_modules_dir.as_ref()?; + let node_modules_deno_dir = root_node_modules_dir.join(".deno"); + let Ok(entries) = self.fs.read_dir_sync(&node_modules_deno_dir) else { + return None; + }; + let search_prefix = format!( + "{}@", + normalize_pkg_name_for_node_modules_deno_folder(&req.name) ); + let mut best_version = None; + + // example entries: + // - @denotest+add@1.0.0 + // - @denotest+add@1.0.0_1 + for entry in entries { + if !entry.is_directory { + continue; + } + let Some(version_and_copy_idx) = entry.name.strip_prefix(&search_prefix) + else { + continue; + }; + let version = version_and_copy_idx + .rsplit_once('_') + .map(|(v, _)| v) + .unwrap_or(version_and_copy_idx); + let Ok(version) = Version::parse_from_npm(version) else { + continue; + }; + if req.version_req.matches(&version) { + if let Some((best_version_version, _)) = &best_version { + if version > *best_version_version { + best_version = Some((version, entry.name)); + } + } else { + best_version = Some((version, entry.name)); + } + } + } + + best_version.map(|(_version, entry_name)| { + join_package_name( + &node_modules_deno_dir.join(entry_name).join("node_modules"), + &req.name, + ) + }) } } @@ -288,29 +334,62 @@ impl CliNpmResolver for ByonmCliNpmResolver { req: &PackageReq, referrer: &ModuleSpecifier, ) -> Result<PathBuf, AnyError> { - // resolve the pkg json and alias - let (pkg_json, alias) = - self.resolve_pkg_json_and_alias_for_req(req, referrer)?; - // now try node resolution - for ancestor in pkg_json.path.parent().unwrap().ancestors() { - let node_modules_folder = ancestor.join("node_modules"); - let sub_dir = join_package_name(&node_modules_folder, &alias); - if self.fs.is_dir_sync(&sub_dir) { - return Ok(canonicalize_path_maybe_not_exists_with_fs( - &sub_dir, - self.fs.as_ref(), - )?); + fn node_resolve_dir( + fs: &dyn FileSystem, + alias: &str, + start_dir: &Path, + ) -> Result<Option<PathBuf>, AnyError> { + for ancestor in start_dir.ancestors() { + let node_modules_folder = ancestor.join("node_modules"); + let sub_dir = join_package_name(&node_modules_folder, alias); + if fs.is_dir_sync(&sub_dir) { + return Ok(Some(canonicalize_path_maybe_not_exists_with_fs( + &sub_dir, fs, + )?)); + } } + Ok(None) } - bail!( - concat!( - "Could not find \"{}\" in a node_modules folder. ", - "Deno expects the node_modules/ directory to be up to date. ", - "Did you forget to run `deno install`?" - ), - alias, - ); + // now attempt to resolve if it's found in any package.json + let maybe_pkg_json_and_alias = + self.resolve_pkg_json_and_alias_for_req(req, referrer)?; + match maybe_pkg_json_and_alias { + Some((pkg_json, alias)) => { + // now try node resolution + if let Some(resolved) = + node_resolve_dir(self.fs.as_ref(), &alias, pkg_json.dir_path())? + { + return Ok(resolved); + } + + bail!( + concat!( + "Could not find \"{}\" in a node_modules folder. ", + "Deno expects the node_modules/ directory to be up to date. ", + "Did you forget to run `deno install`?" + ), + alias, + ); + } + None => { + // now check if node_modules/.deno/ matches this constraint + if let Some(folder) = self.resolve_folder_in_root_node_modules(req) { + return Ok(folder); + } + + bail!( + concat!( + "Could not find a matching package for 'npm:{}' in the node_modules ", + "directory. Ensure you have all your JSR and npm dependencies listed ", + "in your deno.json or package.json, then run `deno install`. Alternatively, ", + r#"turn on auto-install by specifying `"nodeModulesDir": "auto"` in your "#, + "deno.json file." + ), + req, + ); + } + } } fn check_state_hash(&self) -> Option<u64> { |