use std::cmp::Ordering;
use std::path::Path;
use std::path::PathBuf;
use deno_npm::NpmPackageCacheFolderId;
use deno_npm::NpmPackageId;
use deno_npm::resolution::NpmResolutionSnapshot;
use deno_semver::Version;
use node_resolver::NpmPackageFolderResolver;
use node_resolver::UrlOrPathRef;
use sys_traits::FsCanonicalize;
use sys_traits::FsMetadata;
use url::Url;
#[derive(Debug)]
pub enum NpmPackageFsResolver<TSys: FsCanonicalize + FsMetadata> {
Local(super::local::LocalNpmPackageResolver<TSys>),
Global(super::global::GlobalNpmPackageResolver<TSys>),
}
impl<TSys: FsCanonicalize + FsMetadata> NpmPackageFsResolver<TSys> {
pub fn node_modules_path(&self) -> Option<&Path> {
match self {
NpmPackageFsResolver::Local(resolver) => resolver.node_modules_path(),
NpmPackageFsResolver::Global(_) => None,
}
}
pub fn maybe_package_folder(
&self,
package_id: &NpmPackageId,
) -> Option<PathBuf> {
match self {
NpmPackageFsResolver::Local(resolver) => {
resolver.maybe_package_folder(package_id)
}
NpmPackageFsResolver::Global(resolver) => {
resolver.maybe_package_folder(package_id)
}
}
}
pub fn resolve_package_cache_folder_id_from_specifier(
&self,
specifier: &Url,
) -> Result<Option<NpmPackageCacheFolderId>, std::io::Error> {
match self {
NpmPackageFsResolver::Local(resolver) => {
resolver.resolve_package_cache_folder_id_from_specifier(specifier)
}
NpmPackageFsResolver::Global(resolver) => {
resolver.resolve_package_cache_folder_id_from_specifier(specifier)
}
}
}
}
impl<TSys: FsCanonicalize + FsMetadata> NpmPackageFolderResolver
for NpmPackageFsResolver<TSys>
{
fn resolve_package_folder_from_package(
&self,
specifier: &str,
referrer: &UrlOrPathRef,
) -> Result<PathBuf, node_resolver::errors::PackageFolderResolveError> {
match self {
NpmPackageFsResolver::Local(r) => {
r.resolve_package_folder_from_package(specifier, referrer)
}
NpmPackageFsResolver::Global(r) => {
r.resolve_package_folder_from_package(specifier, referrer)
}
}
}
fn resolve_types_package_folder(
&self,
types_package_name: &str,
maybe_package_version: Option<&Version>,
maybe_referrer: Option<&UrlOrPathRef>,
) -> Option<PathBuf> {
match self {
NpmPackageFsResolver::Local(r) => r.resolve_types_package_folder(
types_package_name,
maybe_package_version,
maybe_referrer,
),
NpmPackageFsResolver::Global(r) => r.resolve_types_package_folder(
types_package_name,
maybe_package_version,
maybe_referrer,
),
}
}
}
pub fn find_definitely_typed_package_from_snapshot<'a>(
types_package_name: &str,
maybe_package_version: Option<&Version>,
snapshot: &'a NpmResolutionSnapshot,
) -> Option<&'a NpmPackageId> {
fn is_id_higher_than_id(new: &NpmPackageId, existing: &NpmPackageId) -> bool {
match new.nv.version.cmp(&existing.nv.version) {
Ordering::Equal => new.peer_dependencies > existing.peer_dependencies,
Ordering::Greater => true,
Ordering::Less => false,
}
}
let mut best_patch = 0;
let mut highest: Option<&NpmPackageId> = None;
let mut best: Option<&NpmPackageId> = None;
let all_ids = snapshot
.top_level_packages()
.chain(snapshot.all_packages_for_every_system().map(|pkg| &pkg.id));
for id in all_ids {
if id.nv.name != types_package_name {
continue;
}
if let Some(package_version) = maybe_package_version
&& id.nv.version.major == package_version.major
&& id.nv.version.minor == package_version.minor
&& id.nv.version.patch >= best_patch
&& id.nv.version.pre == package_version.pre
{
let should_replace = match &best {
Some(best_id) => is_id_higher_than_id(id, best_id),
None => true,
};
if should_replace {
best = Some(id);
best_patch = id.nv.version.patch;
}
}
let should_replace = match &highest {
Some(highest_id) => is_id_higher_than_id(id, highest_id),
None => true,
};
if should_replace {
highest = Some(id);
}
}
best.or(highest)
}