0
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2025-02-01 12:16:11 -05:00

perf(node_resolver): reduce url to/from path conversions (#27839)

Extracted out of https://github.com/denoland/deno/pull/27838/files

Reduces some allocations by accepting either a pathbuf or url for the
referrer for resolution and returning either a pathbuf or url at the
end, which the caller can then convert into to their preferred state.

This is about 4% faster when still converting the final result to a url
and 6% faster when keeping the result as a path in a benchmark I ran.
This commit is contained in:
David Sherret 2025-01-27 15:23:20 -05:00 committed by GitHub
parent 92dce12af7
commit 679902a108
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
28 changed files with 679 additions and 417 deletions

1
Cargo.lock generated
View file

@ -5260,6 +5260,7 @@ dependencies = [
"anyhow",
"async-trait",
"boxed_error",
"dashmap",
"deno_error",
"deno_package_json",
"deno_path_util",

View file

@ -7,6 +7,8 @@ use std::sync::Arc;
use deno_core::error::JsError;
use deno_node::NodeRequireLoaderRc;
use deno_path_util::url_from_file_path;
use deno_path_util::url_to_file_path;
use deno_resolver::npm::DenoInNpmPackageChecker;
use deno_resolver::npm::NpmResolver;
use deno_runtime::colors;
@ -44,6 +46,7 @@ use deno_runtime::WorkerExecutionMode;
use deno_runtime::WorkerLogLevel;
use deno_runtime::UNSTABLE_GRANULAR_FLAGS;
use node_resolver::errors::ResolvePkgJsonBinExportError;
use node_resolver::UrlOrPath;
use url::Url;
use crate::args::has_trace_permissions_enabled;
@ -135,6 +138,9 @@ pub fn create_isolate_create_params() -> Option<v8::CreateParams> {
#[derive(Debug, thiserror::Error, deno_error::JsError)]
pub enum ResolveNpmBinaryEntrypointError {
#[class(inherit)]
#[error(transparent)]
PathToUrl(#[from] deno_path_util::PathToUrlError),
#[class(inherit)]
#[error(transparent)]
ResolvePkgJsonBinExport(ResolvePkgJsonBinExportError),
@ -153,7 +159,7 @@ pub enum ResolveNpmBinaryEntrypointFallbackError {
PackageSubpathResolve(node_resolver::errors::PackageSubpathResolveError),
#[class(generic)]
#[error("Cannot find module '{0}'")]
ModuleNotFound(Url),
ModuleNotFound(UrlOrPath),
}
pub struct LibMainWorkerOptions {
@ -525,13 +531,13 @@ impl<TSys: DenoLibSys> LibMainWorkerFactory<TSys> {
.node_resolver
.resolve_binary_export(package_folder, sub_path)
{
Ok(specifier) => Ok(specifier),
Ok(path) => Ok(url_from_file_path(&path)?),
Err(original_err) => {
// if the binary entrypoint was not found, fallback to regular node resolution
let result =
self.resolve_binary_entrypoint_fallback(package_folder, sub_path);
match result {
Ok(Some(specifier)) => Ok(specifier),
Ok(Some(path)) => Ok(url_from_file_path(&path)?),
Ok(None) => {
Err(ResolveNpmBinaryEntrypointError::ResolvePkgJsonBinExport(
original_err,
@ -551,7 +557,7 @@ impl<TSys: DenoLibSys> LibMainWorkerFactory<TSys> {
&self,
package_folder: &Path,
sub_path: Option<&str>,
) -> Result<Option<Url>, ResolveNpmBinaryEntrypointFallbackError> {
) -> Result<Option<PathBuf>, ResolveNpmBinaryEntrypointFallbackError> {
// only fallback if the user specified a sub path
if sub_path.is_none() {
// it's confusing to users if the package doesn't have any binary
@ -573,14 +579,22 @@ impl<TSys: DenoLibSys> LibMainWorkerFactory<TSys> {
.map_err(
ResolveNpmBinaryEntrypointFallbackError::PackageSubpathResolve,
)?;
if deno_path_util::url_to_file_path(&specifier)
.map(|p| self.shared.sys.fs_exists_no_err(p))
.unwrap_or(false)
{
Ok(Some(specifier))
let path = match specifier {
UrlOrPath::Url(ref url) => match url_to_file_path(url) {
Ok(path) => path,
Err(_) => {
return Err(ResolveNpmBinaryEntrypointFallbackError::ModuleNotFound(
specifier,
));
}
},
UrlOrPath::Path(path) => path,
};
if self.shared.sys.fs_exists_no_err(&path) {
Ok(Some(path))
} else {
Err(ResolveNpmBinaryEntrypointFallbackError::ModuleNotFound(
specifier,
UrlOrPath::Path(path),
))
}
}

View file

@ -449,9 +449,7 @@ impl<'a> TsResponseImportMapper<'a> {
.pkg_json_resolver(specifier)
// the specifier might have a closer package.json, but we
// want the root of the package's package.json
.get_closest_package_json_from_file_path(
&package_root_folder.join("package.json"),
)
.get_closest_package_json(&package_root_folder.join("package.json"))
.ok()
.flatten()?;
let root_folder = package_json.path.parent()?;

View file

@ -207,6 +207,8 @@ impl LspScopeResolver {
NodeResolutionKind::Execution,
)
})
.ok()?
.into_url()
.ok()?,
))
.0;
@ -257,7 +259,7 @@ impl LspScopeResolver {
root_node_modules_dir: byonm_npm_resolver
.root_node_modules_path()
.map(|p| p.to_path_buf()),
sys: CliSys::default(),
sys: factory.sys.clone(),
pkg_json_resolver: self.pkg_json_resolver.clone(),
},
)
@ -522,6 +524,8 @@ impl LspResolver {
resolution_mode,
NodeResolutionKind::Types,
)
.ok()?
.into_url()
.ok()?,
)))
}
@ -702,7 +706,7 @@ impl<'a> ResolverFactory<'a> {
let sys = CliSys::default();
let options = if enable_byonm {
CliNpmResolverCreateOptions::Byonm(CliByonmNpmResolverCreateOptions {
sys,
sys: self.sys.clone(),
pkg_json_resolver: self.pkg_json_resolver.clone(),
root_node_modules_dir: self.config_data.and_then(|config_data| {
config_data.node_modules_dir.clone().or_else(|| {

View file

@ -667,7 +667,12 @@ impl<TGraphContainer: ModuleGraphContainer>
ResolutionMode::Import,
NodeResolutionKind::Execution,
)
.map_err(|e| JsErrorBox::from_err(e).into());
.map_err(|e| JsErrorBox::from_err(e).into())
.and_then(|url_or_path| {
url_or_path
.into_url()
.map_err(|e| JsErrorBox::from_err(e).into())
});
}
}
@ -696,6 +701,8 @@ impl<TGraphContainer: ModuleGraphContainer>
source,
})
})?
.into_url()
.map_err(JsErrorBox::from_err)?
}
Some(Module::Node(module)) => module.specifier.clone(),
Some(Module::Js(module)) => module.specifier.clone(),

View file

@ -2,6 +2,7 @@
//! Code for local node_modules resolution.
use std::borrow::Cow;
use std::cell::RefCell;
use std::cmp::Ordering;
use std::collections::hash_map::Entry;
@ -312,7 +313,7 @@ async fn sync_resolution_with_fs(
);
let sub_node_modules = folder_path.join("node_modules");
let package_path =
join_package_name(&sub_node_modules, &package.id.nv.name);
join_package_name(Cow::Owned(sub_node_modules), &package.id.nv.name);
let cache_folder = cache.package_folder_for_nv(&package.id.nv);
deno_core::unsync::spawn_blocking({
@ -350,7 +351,7 @@ async fn sync_resolution_with_fs(
let sub_node_modules = folder_path.join("node_modules");
let package_path =
join_package_name(&sub_node_modules, &package.id.nv.name);
join_package_name(Cow::Owned(sub_node_modules), &package.id.nv.name);
lifecycle_scripts.add(package, package_path.into());
}
@ -367,14 +368,16 @@ async fn sync_resolution_with_fs(
if !initialized_file.exists() {
let sub_node_modules = destination_path.join("node_modules");
let package_path =
join_package_name(&sub_node_modules, &package.id.nv.name);
join_package_name(Cow::Owned(sub_node_modules), &package.id.nv.name);
let source_path = join_package_name(
&deno_local_registry_dir
.join(get_package_folder_id_folder_name(
&package_cache_folder_id.with_no_count(),
))
.join("node_modules"),
Cow::Owned(
deno_local_registry_dir
.join(get_package_folder_id_folder_name(
&package_cache_folder_id.with_no_count(),
))
.join("node_modules"),
),
&package.id.nv.name,
);
@ -407,14 +410,16 @@ async fn sync_resolution_with_fs(
get_package_folder_id_folder_name(&dep_cache_folder_id);
if dep_setup_cache.insert(name, &dep_folder_name) {
let dep_folder_path = join_package_name(
&deno_local_registry_dir
.join(dep_folder_name)
.join("node_modules"),
Cow::Owned(
deno_local_registry_dir
.join(dep_folder_name)
.join("node_modules"),
),
&dep_id.nv.name,
);
symlink_package_dir(
&dep_folder_path,
&join_package_name(&sub_node_modules, name),
&join_package_name(Cow::Borrowed(&sub_node_modules), name),
)?;
}
}
@ -468,9 +473,11 @@ async fn sync_resolution_with_fs(
&remote_pkg.get_package_cache_folder_id(),
);
let local_registry_package_path = join_package_name(
&deno_local_registry_dir
.join(&target_folder_name)
.join("node_modules"),
Cow::Owned(
deno_local_registry_dir
.join(&target_folder_name)
.join("node_modules"),
),
&remote_pkg.id.nv.name,
);
if install_in_child {
@ -496,7 +503,10 @@ async fn sync_resolution_with_fs(
{
symlink_package_dir(
&local_registry_package_path,
&join_package_name(root_node_modules_dir_path, remote_alias),
&join_package_name(
Cow::Borrowed(root_node_modules_dir_path),
remote_alias,
),
)?;
}
}
@ -526,15 +536,20 @@ async fn sync_resolution_with_fs(
get_package_folder_id_folder_name(&package.get_package_cache_folder_id());
if setup_cache.insert_root_symlink(&id.nv.name, &target_folder_name) {
let local_registry_package_path = join_package_name(
&deno_local_registry_dir
.join(target_folder_name)
.join("node_modules"),
Cow::Owned(
deno_local_registry_dir
.join(target_folder_name)
.join("node_modules"),
),
&id.nv.name,
);
symlink_package_dir(
&local_registry_package_path,
&join_package_name(root_node_modules_dir_path, &id.nv.name),
&join_package_name(
Cow::Borrowed(root_node_modules_dir_path),
&id.nv.name,
),
)?;
}
}
@ -556,15 +571,20 @@ async fn sync_resolution_with_fs(
if setup_cache.insert_deno_symlink(&package.id.nv.name, &target_folder_name)
{
let local_registry_package_path = join_package_name(
&deno_local_registry_dir
.join(target_folder_name)
.join("node_modules"),
Cow::Owned(
deno_local_registry_dir
.join(target_folder_name)
.join("node_modules"),
),
&package.id.nv.name,
);
symlink_package_dir(
&local_registry_package_path,
&join_package_name(&deno_node_modules_dir, &package.id.nv.name),
&join_package_name(
Cow::Borrowed(&deno_node_modules_dir),
&package.id.nv.name,
),
)?;
}
}
@ -986,13 +1006,17 @@ fn junction_or_symlink_dir(
}
}
fn join_package_name(path: &Path, package_name: &str) -> PathBuf {
let mut path = path.to_path_buf();
fn join_package_name(mut path: Cow<Path>, package_name: &str) -> PathBuf {
// ensure backslashes are used on windows
for part in package_name.split('/') {
path = path.join(part);
match path {
Cow::Borrowed(inner) => path = Cow::Owned(inner.join(part)),
Cow::Owned(ref mut path) => {
path.push(part);
}
}
}
path
path.into_owned()
}
#[cfg(test)]

View file

@ -196,8 +196,8 @@ impl ModuleLoader for EmbeddedModuleLoader {
referrer_kind,
NodeResolutionKind::Execution,
)
.map_err(JsErrorBox::from_err)?
.into_url(),
.and_then(|res| res.into_url())
.map_err(JsErrorBox::from_err)?,
);
}
@ -225,7 +225,10 @@ impl ModuleLoader for EmbeddedModuleLoader {
referrer_kind,
NodeResolutionKind::Execution,
)
.map_err(JsErrorBox::from_err)?,
.map_err(JsErrorBox::from_err)
.and_then(|url_or_path| {
url_or_path.into_url().map_err(JsErrorBox::from_err)
})?,
),
Ok(MappedResolution::PackageJson {
dep_result,
@ -236,17 +239,22 @@ impl ModuleLoader for EmbeddedModuleLoader {
.as_ref()
.map_err(|e| JsErrorBox::from_err(e.clone()))?
{
PackageJsonDepValue::Req(req) => self
.shared
.npm_req_resolver
.resolve_req_with_sub_path(
req,
sub_path.as_deref(),
&referrer,
referrer_kind,
NodeResolutionKind::Execution,
)
.map_err(|e| JsErrorBox::from_err(e).into()),
PackageJsonDepValue::Req(req) => Ok(
self
.shared
.npm_req_resolver
.resolve_req_with_sub_path(
req,
sub_path.as_deref(),
&referrer,
referrer_kind,
NodeResolutionKind::Execution,
)
.map_err(JsErrorBox::from_err)
.and_then(|url_or_path| {
url_or_path.into_url().map_err(JsErrorBox::from_err)
})?,
),
PackageJsonDepValue::Workspace(version_req) => {
let pkg_folder = self
.shared
@ -267,7 +275,10 @@ impl ModuleLoader for EmbeddedModuleLoader {
referrer_kind,
NodeResolutionKind::Execution,
)
.map_err(JsErrorBox::from_err)?,
.map_err(JsErrorBox::from_err)
.and_then(|url_or_path| {
url_or_path.into_url().map_err(JsErrorBox::from_err)
})?,
)
}
},
@ -286,7 +297,10 @@ impl ModuleLoader for EmbeddedModuleLoader {
referrer_kind,
NodeResolutionKind::Execution,
)
.map_err(JsErrorBox::from_err)?,
.map_err(JsErrorBox::from_err)
.and_then(|url_or_path| {
url_or_path.into_url().map_err(JsErrorBox::from_err)
})?,
);
}
@ -323,7 +337,7 @@ impl ModuleLoader for EmbeddedModuleLoader {
)
.map_err(JsErrorBox::from_err)?;
if let Some(res) = maybe_res {
return Ok(res.into_url());
return Ok(res.into_url().map_err(JsErrorBox::from_err)?);
}
Err(JsErrorBox::from_err(err).into())
}

View file

@ -667,7 +667,7 @@ fn resolve_npm_nv_ref(
node_resolver::NodeResolutionKind::Types,
)
.ok()?;
Some(resolved)
resolved.into_url().ok()
}
/// Matches the `@ts-check` pragma.

View file

@ -75,13 +75,17 @@ pub async fn info(
target_pkg_json,
sub_path,
..
} => Some(node_resolver.resolve_package_subpath_from_deno_module(
target_pkg_json.clone().dir_path(),
sub_path.as_deref(),
Some(&cwd_url),
node_resolver::ResolutionMode::Import,
node_resolver::NodeResolutionKind::Execution,
)?),
} => Some(
node_resolver
.resolve_package_subpath_from_deno_module(
target_pkg_json.clone().dir_path(),
sub_path.as_deref(),
Some(&cwd_url),
node_resolver::ResolutionMode::Import,
node_resolver::NodeResolutionKind::Execution,
)?
.into_url()?,
),
deno_config::workspace::MappedResolution::PackageJson {
alias,
sub_path,
@ -94,13 +98,17 @@ pub async fn info(
alias,
version_req,
)?;
Some(node_resolver.resolve_package_subpath_from_deno_module(
pkg_folder,
sub_path.as_deref(),
Some(&cwd_url),
node_resolver::ResolutionMode::Import,
node_resolver::NodeResolutionKind::Execution,
)?)
Some(
node_resolver
.resolve_package_subpath_from_deno_module(
pkg_folder,
sub_path.as_deref(),
Some(&cwd_url),
node_resolver::ResolutionMode::Import,
node_resolver::NodeResolutionKind::Execution,
)?
.into_url()?,
)
}
deno_package_json::PackageJsonDepValue::Req(req) => {
Some(ModuleSpecifier::parse(&format!(

View file

@ -709,6 +709,9 @@ pub enum ResolveError {
)]
ModuleResolution(#[from] deno_core::ModuleResolutionError),
#[class(inherit)]
#[error(transparent)]
FilePathToUrl(#[from] deno_path_util::PathToUrlError),
#[class(inherit)]
#[error("{0}")]
PackageSubpathResolve(PackageSubpathResolveError),
#[class(inherit)]
@ -943,7 +946,7 @@ fn resolve_graph_specifier_types(
NodeResolutionKind::Types,
);
let maybe_url = match res_result {
Ok(url) => Some(url),
Ok(path_or_url) => Some(path_or_url.into_url()?),
Err(err) => match err.code() {
NodeJsErrorCode::ERR_TYPES_NOT_FOUND
| NodeJsErrorCode::ERR_MODULE_NOT_FOUND => None,
@ -971,6 +974,9 @@ fn resolve_graph_specifier_types(
#[derive(Debug, Error, deno_error::JsError)]
pub enum ResolveNonGraphSpecifierTypesError {
#[class(inherit)]
#[error(transparent)]
FilePathToUrl(#[from] deno_path_util::PathToUrlError),
#[class(inherit)]
#[error(transparent)]
ResolvePkgFolderFromDenoReq(#[from] ResolvePkgFolderFromDenoReqError),
@ -1003,8 +1009,8 @@ fn resolve_non_graph_specifier_types(
resolution_mode,
NodeResolutionKind::Types,
)
.ok()
.map(|res| res.into_url()),
.and_then(|res| res.into_url())
.ok(),
)))
} else if let Ok(npm_req_ref) =
NpmPackageReqReference::from_str(raw_specifier)
@ -1025,7 +1031,7 @@ fn resolve_non_graph_specifier_types(
NodeResolutionKind::Types,
);
let maybe_url = match res_result {
Ok(url) => Some(url),
Ok(url_or_path) => Some(url_or_path.into_url()?),
Err(err) => match err.code() {
NodeJsErrorCode::ERR_TYPES_NOT_FOUND
| NodeJsErrorCode::ERR_MODULE_NOT_FOUND => None,

View file

@ -23,6 +23,8 @@ use node_resolver::InNpmPackageChecker;
use node_resolver::NodeResolutionKind;
use node_resolver::NpmPackageFolderResolver;
use node_resolver::ResolutionMode;
use node_resolver::UrlOrPath;
use node_resolver::UrlOrPathRef;
use node_resolver::REQUIRE_CONDITIONS;
use sys_traits::FsCanonicalize;
use sys_traits::FsMetadata;
@ -277,11 +279,12 @@ pub fn op_require_resolve_deno_dir<
TSys,
>>();
let path = PathBuf::from(parent_filename);
Ok(
resolver
.resolve_package_folder_from_package(
&request,
&url_from_file_path(&PathBuf::from(parent_filename))?,
&UrlOrPathRef::from_path(&path),
)
.ok()
.map(|p| p.to_string_lossy().into_owned()),
@ -487,9 +490,7 @@ pub fn op_require_try_self<
let pkg_json_resolver = state.borrow::<PackageJsonResolverRc<TSys>>();
let pkg = pkg_json_resolver
.get_closest_package_json_from_file_path(&PathBuf::from(
parent_path.unwrap(),
))
.get_closest_package_json(&PathBuf::from(parent_path.unwrap()))
.ok()
.flatten();
if pkg.is_none() {
@ -515,13 +516,13 @@ pub fn op_require_try_self<
return Ok(None);
}
let referrer = deno_core::url::Url::from_file_path(&pkg.path).unwrap();
if let Some(exports) = &pkg.exports {
let node_resolver = state.borrow::<NodeResolverRc<
TInNpmPackageChecker,
TNpmPackageFolderResolver,
TSys,
>>();
let referrer = UrlOrPathRef::from_path(&pkg.path);
let r = node_resolver.package_exports_resolve(
&pkg.path,
&expansion,
@ -531,11 +532,7 @@ pub fn op_require_try_self<
REQUIRE_CONDITIONS,
NodeResolutionKind::Execution,
)?;
Ok(Some(if r.scheme() == "file" {
url_to_file_path_string(&r)?
} else {
r.to_string()
}))
Ok(Some(url_or_path_to_string(r)?))
} else {
Ok(None)
}
@ -627,22 +624,21 @@ pub fn op_require_resolve_exports<
let referrer = if parent_path.is_empty() {
None
} else {
Some(Url::from_file_path(parent_path).unwrap())
Some(PathBuf::from(parent_path))
};
let r = node_resolver.package_exports_resolve(
&pkg.path,
&format!(".{expansion}"),
exports,
referrer.as_ref(),
referrer
.as_ref()
.map(|r| UrlOrPathRef::from_path(r))
.as_ref(),
ResolutionMode::Require,
REQUIRE_CONDITIONS,
NodeResolutionKind::Execution,
)?;
Ok(Some(if r.scheme() == "file" {
url_to_file_path_string(&r)?
} else {
r.to_string()
}))
Ok(Some(url_or_path_to_string(r)?))
}
deno_error::js_error_wrapper!(
@ -701,8 +697,7 @@ pub fn op_require_package_imports_resolve<
let referrer_path = ensure_read_permission::<P>(state, &referrer_path)
.map_err(RequireErrorKind::Permission)?;
let pkg_json_resolver = state.borrow::<PackageJsonResolverRc<TSys>>();
let Some(pkg) = pkg_json_resolver
.get_closest_package_json_from_file_path(&referrer_path)?
let Some(pkg) = pkg_json_resolver.get_closest_package_json(&referrer_path)?
else {
return Ok(None);
};
@ -713,16 +708,15 @@ pub fn op_require_package_imports_resolve<
TNpmPackageFolderResolver,
TSys,
>>();
let referrer_url = Url::from_file_path(&referrer_filename).unwrap();
let url = node_resolver.package_imports_resolve(
&request,
Some(&referrer_url),
Some(&UrlOrPathRef::from_path(&referrer_path)),
ResolutionMode::Require,
Some(&pkg),
REQUIRE_CONDITIONS,
NodeResolutionKind::Execution,
)?;
Ok(Some(url_to_file_path_string(&url)?))
Ok(Some(url_or_path_to_string(url)?))
} else {
Ok(None)
}
@ -738,11 +732,6 @@ pub fn op_require_break_on_next_statement(state: Rc<RefCell<OpState>>) {
inspector.wait_for_session_and_break_on_next_statement()
}
fn url_to_file_path_string(url: &Url) -> Result<String, RequireError> {
let file_path = url_to_file_path(url)?;
Ok(file_path.to_string_lossy().into_owned())
}
#[op2(fast)]
pub fn op_require_can_parse_as_esm(
scope: &mut v8::HandleScope,
@ -768,3 +757,13 @@ pub fn op_require_can_parse_as_esm(
let mut source = v8::script_compiler::Source::new(source, Some(&origin));
v8::script_compiler::compile_module(scope, &mut source).is_some()
}
fn url_or_path_to_string(
url: UrlOrPath,
) -> Result<String, deno_path_util::UrlToFilePathError> {
if url.is_file() {
Ok(url.into_path()?.to_string_lossy().to_string())
} else {
Ok(url.to_string_lossy().to_string())
}
}

View file

@ -267,8 +267,11 @@ impl<TInNpmPackageChecker: InNpmPackageChecker, TSys: FsRead>
specifier: &Url,
) -> Result<ResolutionMode, ClosestPkgJsonError> {
if self.in_npm_pkg_checker.in_npm_package(specifier) {
let Ok(path) = deno_path_util::url_to_file_path(specifier) else {
return Ok(ResolutionMode::Require);
};
if let Some(pkg_json) =
self.pkg_json_resolver.get_closest_package_json(specifier)?
self.pkg_json_resolver.get_closest_package_json(&path)?
{
let is_file_location_cjs = pkg_json.typ != "module";
Ok(if is_file_location_cjs {
@ -280,8 +283,11 @@ impl<TInNpmPackageChecker: InNpmPackageChecker, TSys: FsRead>
Ok(ResolutionMode::Require)
}
} else if self.mode != IsCjsResolutionMode::Disabled {
let Ok(path) = deno_path_util::url_to_file_path(specifier) else {
return Ok(ResolutionMode::Import);
};
if let Some(pkg_json) =
self.pkg_json_resolver.get_closest_package_json(specifier)?
self.pkg_json_resolver.get_closest_package_json(&path)?
{
let is_cjs_type = pkg_json.typ == "commonjs"
|| self.mode == IsCjsResolutionMode::ImplicitTypeCommonJs

View file

@ -639,7 +639,6 @@ impl<
options: ResolverFactoryOptions,
) -> Self {
Self {
options,
deno_resolver: Default::default(),
in_npm_package_checker: Default::default(),
node_resolver: Default::default(),
@ -650,6 +649,7 @@ impl<
sloppy_imports_resolver: Default::default(),
workspace_factory,
workspace_resolver: Default::default(),
options,
}
}

View file

@ -92,6 +92,9 @@ pub enum DenoResolveErrorKind {
PackageSubpathResolve(#[from] PackageSubpathResolveError),
#[class(inherit)]
#[error(transparent)]
PathToUrl(#[from] deno_path_util::PathToUrlError),
#[class(inherit)]
#[error(transparent)]
ResolvePkgFolderFromDenoReq(#[from] ResolvePkgFolderFromDenoReqError),
#[class(inherit)]
#[error(transparent)]
@ -252,10 +255,12 @@ impl<
{
return node_resolver
.resolve(raw_specifier, referrer, resolution_mode, resolution_kind)
.map(|res| DenoResolution {
url: res.into_url(),
found_package_json_dep,
maybe_diagnostic,
.and_then(|res| {
Ok(DenoResolution {
url: res.into_url()?,
found_package_json_dep,
maybe_diagnostic,
})
})
.map_err(|e| e.into());
}
@ -318,7 +323,8 @@ impl<
resolution_mode,
resolution_kind,
)
.map_err(|e| e.into()),
.map_err(DenoResolveError::from)
.and_then(|r| Ok(r.into_url()?)),
MappedResolution::PackageJson {
dep_result,
alias,
@ -372,7 +378,8 @@ impl<
.map_err(|e| {
DenoResolveErrorKind::PackageSubpathResolve(e).into_box()
})
}),
})
.and_then(|r| Ok(r.into_url()?)),
})
}
},
@ -425,12 +432,14 @@ impl<
resolution_mode,
resolution_kind,
)
.map(|url| DenoResolution {
url,
maybe_diagnostic,
found_package_json_dep,
})
.map_err(|e| e.into());
.map_err(DenoResolveError::from)
.and_then(|url_or_path| {
Ok(DenoResolution {
url: url_or_path.into_url()?,
maybe_diagnostic,
found_package_json_dep,
})
});
}
// do npm resolution for byonm
@ -442,11 +451,6 @@ impl<
resolution_mode,
resolution_kind,
)
.map(|url| DenoResolution {
url,
maybe_diagnostic,
found_package_json_dep,
})
.map_err(|err| {
match err.into_kind() {
ResolveReqWithSubPathErrorKind::MissingPackageNodeModulesFolder(
@ -459,7 +463,12 @@ impl<
err.into()
}
}
});
})
.and_then(|url_or_path| Ok(DenoResolution {
url: url_or_path.into_url()?,
maybe_diagnostic,
found_package_json_dep,
}));
}
}
@ -491,9 +500,9 @@ impl<
})?;
if let Some(res) = maybe_resolution {
match res {
NodeResolution::Module(url) => {
NodeResolution::Module(ref _url) => {
return Ok(DenoResolution {
url,
url: res.into_url()?,
maybe_diagnostic,
found_package_json_dep,
})

View file

@ -18,6 +18,7 @@ use node_resolver::errors::PackageNotFoundError;
use node_resolver::InNpmPackageChecker;
use node_resolver::NpmPackageFolderResolver;
use node_resolver::PackageJsonResolverRc;
use node_resolver::UrlOrPathRef;
use sys_traits::FsCanonicalize;
use sys_traits::FsDirEntry;
use sys_traits::FsMetadata;
@ -141,7 +142,7 @@ impl<TSys: FsCanonicalize + FsRead + FsMetadata + FsReadDir>
) -> std::io::Result<Option<PathBuf>> {
for ancestor in start_dir.ancestors() {
let node_modules_folder = ancestor.join("node_modules");
let sub_dir = join_package_name(&node_modules_folder, alias);
let sub_dir = join_package_name(Cow::Owned(node_modules_folder), alias);
if sys.fs_is_dir_no_err(&sub_dir) {
return Ok(Some(
deno_path_util::fs::canonicalize_path_maybe_not_exists(
@ -368,7 +369,7 @@ impl<TSys: FsCanonicalize + FsRead + FsMetadata + FsReadDir>
best_version.map(|(_version, entry_name)| {
join_package_name(
&node_modules_deno_dir.join(entry_name).join("node_modules"),
Cow::Owned(node_modules_deno_dir.join(entry_name).join("node_modules")),
&req.name,
)
})
@ -381,14 +382,14 @@ impl<TSys: FsCanonicalize + FsMetadata + FsRead + FsReadDir>
fn resolve_package_folder_from_package(
&self,
name: &str,
referrer: &Url,
referrer: &UrlOrPathRef,
) -> Result<PathBuf, PackageFolderResolveError> {
fn inner<TSys: FsMetadata>(
sys: &TSys,
name: &str,
referrer: &Url,
referrer: &UrlOrPathRef,
) -> Result<PathBuf, PackageFolderResolveError> {
let maybe_referrer_file = url_to_file_path(referrer).ok();
let maybe_referrer_file = referrer.path().ok();
let maybe_start_folder =
maybe_referrer_file.as_ref().and_then(|f| f.parent());
if let Some(start_folder) = maybe_start_folder {
@ -400,7 +401,7 @@ impl<TSys: FsCanonicalize + FsMetadata + FsRead + FsReadDir>
Cow::Owned(current_folder.join("node_modules"))
};
let sub_dir = join_package_name(&node_modules_folder, name);
let sub_dir = join_package_name(node_modules_folder, name);
if sys.fs_is_dir_no_err(&sub_dir) {
return Ok(sub_dir);
}
@ -410,7 +411,7 @@ impl<TSys: FsCanonicalize + FsMetadata + FsRead + FsReadDir>
Err(
PackageNotFoundError {
package_name: name.to_string(),
referrer: referrer.clone(),
referrer: referrer.display(),
referrer_extra: None,
}
.into(),
@ -421,7 +422,7 @@ impl<TSys: FsCanonicalize + FsMetadata + FsRead + FsReadDir>
self.sys.fs_canonicalize(&path).map_err(|err| {
PackageFolderResolveIoError {
package_name: name.to_string(),
referrer: referrer.clone(),
referrer: referrer.display(),
source: err,
}
.into()
@ -442,11 +443,15 @@ impl InNpmPackageChecker for ByonmInNpmPackageChecker {
}
}
fn join_package_name(path: &Path, package_name: &str) -> PathBuf {
let mut path = path.to_path_buf();
fn join_package_name(mut path: Cow<Path>, package_name: &str) -> PathBuf {
// ensure backslashes are used on windows
for part in package_name.split('/') {
path = path.join(part);
match path {
Cow::Borrowed(inner) => path = Cow::Owned(inner.join(part)),
Cow::Owned(ref mut path) => {
path.push(part);
}
}
}
path
path.into_owned()
}

View file

@ -6,6 +6,7 @@ use std::path::PathBuf;
use deno_npm::NpmPackageCacheFolderId;
use deno_npm::NpmPackageId;
use node_resolver::NpmPackageFolderResolver;
use node_resolver::UrlOrPathRef;
use sys_traits::FsCanonicalize;
use sys_traits::FsMetadata;
use url::Url;
@ -60,7 +61,7 @@ impl<TSys: FsCanonicalize + FsMetadata> NpmPackageFolderResolver
fn resolve_package_folder_from_package(
&self,
specifier: &str,
referrer: &Url,
referrer: &UrlOrPathRef,
) -> Result<PathBuf, node_resolver::errors::PackageFolderResolveError> {
match self {
NpmPackageFsResolver::Local(r) => {

View file

@ -13,6 +13,7 @@ use node_resolver::errors::PackageFolderResolveError;
use node_resolver::errors::PackageNotFoundError;
use node_resolver::errors::ReferrerNotFoundError;
use node_resolver::NpmPackageFolderResolver;
use node_resolver::UrlOrPathRef;
use url::Url;
use super::resolution::NpmResolutionCellRc;
@ -83,15 +84,15 @@ impl NpmPackageFolderResolver for GlobalNpmPackageResolver {
fn resolve_package_folder_from_package(
&self,
name: &str,
referrer: &Url,
referrer: &UrlOrPathRef,
) -> Result<PathBuf, PackageFolderResolveError> {
use deno_npm::resolution::PackageNotFoundFromReferrerError;
let Some(referrer_cache_folder_id) =
self.resolve_package_cache_folder_id_from_specifier_inner(referrer)
let Some(referrer_cache_folder_id) = self
.resolve_package_cache_folder_id_from_specifier_inner(referrer.url()?)
else {
return Err(
ReferrerNotFoundError {
referrer: referrer.clone(),
referrer: referrer.display(),
referrer_extra: None,
}
.into(),
@ -106,7 +107,7 @@ impl NpmPackageFolderResolver for GlobalNpmPackageResolver {
None => Err(
PackageNotFoundError {
package_name: name.to_string(),
referrer: referrer.clone(),
referrer: referrer.display(),
referrer_extra: Some(format!(
"{} -> {}",
referrer_cache_folder_id,
@ -119,7 +120,7 @@ impl NpmPackageFolderResolver for GlobalNpmPackageResolver {
Err(err) => match *err {
PackageNotFoundFromReferrerError::Referrer(cache_folder_id) => Err(
ReferrerNotFoundError {
referrer: referrer.clone(),
referrer: referrer.display(),
referrer_extra: Some(cache_folder_id.to_string()),
}
.into(),
@ -130,7 +131,7 @@ impl NpmPackageFolderResolver for GlobalNpmPackageResolver {
} => Err(
PackageNotFoundError {
package_name: name,
referrer: referrer.clone(),
referrer: referrer.display(),
referrer_extra: Some(cache_folder_id_referrer.to_string()),
}
.into(),

View file

@ -15,6 +15,7 @@ use node_resolver::errors::PackageFolderResolveIoError;
use node_resolver::errors::PackageNotFoundError;
use node_resolver::errors::ReferrerNotFoundError;
use node_resolver::NpmPackageFolderResolver;
use node_resolver::UrlOrPathRef;
use sys_traits::FsCanonicalize;
use sys_traits::FsMetadata;
use url::Url;
@ -149,19 +150,19 @@ impl<TSys: FsCanonicalize + FsMetadata> NpmPackageFolderResolver
fn resolve_package_folder_from_package(
&self,
name: &str,
referrer: &Url,
referrer: &UrlOrPathRef,
) -> Result<PathBuf, PackageFolderResolveError> {
let maybe_local_path = self
.resolve_folder_for_specifier(referrer)
.resolve_folder_for_specifier(referrer.url()?)
.map_err(|err| PackageFolderResolveIoError {
package_name: name.to_string(),
referrer: referrer.clone(),
source: err,
})?;
package_name: name.to_string(),
referrer: referrer.display(),
source: err,
})?;
let Some(local_path) = maybe_local_path else {
return Err(
ReferrerNotFoundError {
referrer: referrer.clone(),
referrer: referrer.display(),
referrer_extra: None,
}
.into(),
@ -182,7 +183,7 @@ impl<TSys: FsCanonicalize + FsMetadata> NpmPackageFolderResolver
return Ok(self.sys.fs_canonicalize(&sub_dir).map_err(|err| {
PackageFolderResolveIoError {
package_name: name.to_string(),
referrer: referrer.clone(),
referrer: referrer.display(),
source: err,
}
})?);
@ -196,7 +197,7 @@ impl<TSys: FsCanonicalize + FsMetadata> NpmPackageFolderResolver
Err(
PackageNotFoundError {
package_name: name.to_string(),
referrer: referrer.clone(),
referrer: referrer.display(),
referrer_extra: None,
}
.into(),

View file

@ -19,6 +19,7 @@ use deno_semver::package::PackageNv;
use deno_semver::package::PackageReq;
use node_resolver::InNpmPackageChecker;
use node_resolver::NpmPackageFolderResolver;
use node_resolver::UrlOrPathRef;
use sys_traits::FsCanonicalize;
use sys_traits::FsMetadata;
use url::Url;
@ -242,7 +243,7 @@ impl<TSys: FsCanonicalize + FsMetadata> NpmPackageFolderResolver
fn resolve_package_folder_from_package(
&self,
specifier: &str,
referrer: &Url,
referrer: &UrlOrPathRef,
) -> Result<PathBuf, node_resolver::errors::PackageFolderResolveError> {
let path = self
.fs_resolver
@ -250,7 +251,7 @@ impl<TSys: FsCanonicalize + FsMetadata> NpmPackageFolderResolver
log::debug!(
"Resolved {} from {} to {}",
specifier,
referrer,
referrer.display(),
path.display()
);
Ok(path)

View file

@ -22,6 +22,8 @@ use node_resolver::NodeResolutionKind;
use node_resolver::NodeResolverRc;
use node_resolver::NpmPackageFolderResolver;
use node_resolver::ResolutionMode;
use node_resolver::UrlOrPath;
use node_resolver::UrlOrPathRef;
use sys_traits::FsCanonicalize;
use sys_traits::FsMetadata;
use sys_traits::FsRead;
@ -234,7 +236,7 @@ impl<TSys: FsCanonicalize + FsMetadata + FsRead + FsReadDir>
fn resolve_package_folder_from_package(
&self,
specifier: &str,
referrer: &Url,
referrer: &UrlOrPathRef,
) -> Result<PathBuf, node_resolver::errors::PackageFolderResolveError> {
match self {
NpmResolver::Byonm(byonm_resolver) => {
@ -331,7 +333,7 @@ impl<
referrer: &Url,
resolution_mode: ResolutionMode,
resolution_kind: NodeResolutionKind,
) -> Result<Url, ResolveReqWithSubPathError> {
) -> Result<UrlOrPath, ResolveReqWithSubPathError> {
self.resolve_req_with_sub_path(
req_ref.req(),
req_ref.sub_path(),
@ -348,7 +350,7 @@ impl<
referrer: &Url,
resolution_mode: ResolutionMode,
resolution_kind: NodeResolutionKind,
) -> Result<Url, ResolveReqWithSubPathError> {
) -> Result<UrlOrPath, ResolveReqWithSubPathError> {
let package_folder = self
.npm_resolver
.resolve_pkg_folder_from_deno_module_req(req, referrer)?;
@ -398,6 +400,8 @@ impl<
| NodeResolveErrorKind::PackageImportsResolve(_)
| NodeResolveErrorKind::UnsupportedEsmUrlScheme(_)
| NodeResolveErrorKind::DataUrlReferrer(_)
| NodeResolveErrorKind::PathToUrl(_)
| NodeResolveErrorKind::UrlToFilePath(_)
| NodeResolveErrorKind::TypesNotFound(_)
| NodeResolveErrorKind::FinalizeResolution(_) => Err(
ResolveIfForNpmPackageErrorKind::NodeResolve(err.into()).into_box(),
@ -405,6 +409,12 @@ impl<
NodeResolveErrorKind::PackageResolve(err) => {
let err = err.into_kind();
match err {
PackageResolveErrorKind::UrlToFilePath(err) => Err(
ResolveIfForNpmPackageErrorKind::NodeResolve(
NodeResolveErrorKind::UrlToFilePath(err).into_box(),
)
.into_box(),
),
PackageResolveErrorKind::ClosestPkgJson(_)
| PackageResolveErrorKind::InvalidModuleSpecifier(_)
| PackageResolveErrorKind::ExportsResolve(_)
@ -416,6 +426,12 @@ impl<
),
PackageResolveErrorKind::PackageFolderResolve(err) => {
match err.as_kind() {
PackageFolderResolveErrorKind::PathToUrl(err) => Err(
ResolveIfForNpmPackageErrorKind::NodeResolve(
NodeResolveErrorKind::PathToUrl(err.clone()).into_box(),
)
.into_box(),
),
PackageFolderResolveErrorKind::Io(
PackageFolderResolveIoError { package_name, .. },
)

View file

@ -20,6 +20,7 @@ sync = ["deno_package_json/sync"]
anyhow.workspace = true
async-trait.workspace = true
boxed_error.workspace = true
dashmap.workspace = true
deno_error.workspace = true
deno_package_json.workspace = true
deno_path_util.workspace = true

View file

@ -7,7 +7,6 @@ use std::path::Path;
use std::path::PathBuf;
use deno_error::JsErrorBox;
use deno_path_util::url_from_file_path;
use deno_path_util::url_to_file_path;
use futures::future::LocalBoxFuture;
use futures::stream::FuturesUnordered;
@ -29,6 +28,8 @@ use crate::NpmPackageFolderResolver;
use crate::PackageJsonResolverRc;
use crate::PathClean;
use crate::ResolutionMode;
use crate::UrlOrPath;
use crate::UrlOrPathRef;
#[derive(Debug, Clone)]
pub enum CjsAnalysis<'a> {
@ -253,14 +254,21 @@ impl<
errors: &mut Vec<JsErrorBox>| {
// 1. Resolve the re-exports and start a future to analyze each one
for reexport in reexports {
let result = self.resolve(
&reexport,
&referrer,
// FIXME(bartlomieju): check if these conditions are okay, probably
// should be `deno-require`, because `deno` is already used in `esm_resolver.rs`
&["deno", "node", "require", "default"],
NodeResolutionKind::Execution,
);
let result = self
.resolve(
&reexport,
&referrer,
// FIXME(bartlomieju): check if these conditions are okay, probably
// should be `deno-require`, because `deno` is already used in `esm_resolver.rs`
&["deno", "node", "require", "default"],
NodeResolutionKind::Execution,
)
.and_then(|value| {
value
.map(|url_or_path| url_or_path.into_url())
.transpose()
.map_err(JsErrorBox::from_err)
});
let reexport_specifier = match result {
Ok(Some(specifier)) => specifier,
Ok(None) => continue,
@ -355,18 +363,18 @@ impl<
referrer: &Url,
conditions: &[&str],
resolution_kind: NodeResolutionKind,
) -> Result<Option<Url>, JsErrorBox> {
) -> Result<Option<UrlOrPath>, JsErrorBox> {
if specifier.starts_with('/') {
todo!();
}
let referrer_path = url_to_file_path(referrer).unwrap();
let referrer = UrlOrPathRef::from_url(referrer);
let referrer_path = referrer.path().unwrap();
if specifier.starts_with("./") || specifier.starts_with("../") {
if let Some(parent) = referrer_path.parent() {
return self
.file_extension_probe(parent.join(specifier), &referrer_path)
.and_then(|p| url_from_file_path(&p).map_err(JsErrorBox::from_err))
.map(Some);
.file_extension_probe(parent.join(specifier), referrer_path)
.map(|p| Some(UrlOrPath::Path(p)));
} else {
todo!();
}
@ -376,20 +384,21 @@ impl<
let (package_specifier, package_subpath) =
parse_specifier(specifier).unwrap();
let module_dir = match self
.npm_resolver
.resolve_package_folder_from_package(package_specifier.as_str(), referrer)
{
Err(err)
if matches!(
err.as_kind(),
crate::errors::PackageFolderResolveErrorKind::PackageNotFound(..)
) =>
{
return Ok(None);
}
other => other.map_err(JsErrorBox::from_err)?,
};
let module_dir =
match self.npm_resolver.resolve_package_folder_from_package(
package_specifier.as_str(),
&referrer,
) {
Err(err)
if matches!(
err.as_kind(),
crate::errors::PackageFolderResolveErrorKind::PackageNotFound(..)
) =>
{
return Ok(None);
}
other => other.map_err(JsErrorBox::from_err)?,
};
let package_json_path = module_dir.join("package.json");
let maybe_package_json = self
@ -405,7 +414,7 @@ impl<
&package_json_path,
&package_subpath,
exports,
Some(referrer),
Some(&referrer),
ResolutionMode::Import,
conditions,
resolution_kind,
@ -429,39 +438,26 @@ impl<
if let Some(main) =
package_json.main(deno_package_json::NodeModuleKind::Cjs)
{
return Ok(Some(
url_from_file_path(&d.join(main).clean())
.map_err(JsErrorBox::from_err)?,
));
return Ok(Some(UrlOrPath::Path(d.join(main).clean())));
}
}
return Ok(Some(
url_from_file_path(&d.join("index.js").clean())
.map_err(JsErrorBox::from_err)?,
));
return Ok(Some(UrlOrPath::Path(d.join("index.js").clean())));
}
return self
.file_extension_probe(d, &referrer_path)
.and_then(|p| url_from_file_path(&p).map_err(JsErrorBox::from_err))
.map(Some);
.file_extension_probe(d, referrer_path)
.map(|p| Some(UrlOrPath::Path(p)));
} else if let Some(main) =
package_json.main(deno_package_json::NodeModuleKind::Cjs)
{
return Ok(Some(
url_from_file_path(&module_dir.join(main).clean())
.map_err(JsErrorBox::from_err)?,
));
return Ok(Some(UrlOrPath::Path(module_dir.join(main).clean())));
} else {
return Ok(Some(
url_from_file_path(&module_dir.join("index.js").clean())
.map_err(JsErrorBox::from_err)?,
));
return Ok(Some(UrlOrPath::Path(module_dir.join("index.js").clean())));
}
}
// as a fallback, attempt to resolve it via the ancestor directories
let mut last = referrer_path.as_path();
let mut last = referrer_path;
while let Some(parent) = last.parent() {
if !self.in_npm_pkg_checker.in_npm_package_at_dir_path(parent) {
break;
@ -471,15 +467,13 @@ impl<
} else {
parent.join("node_modules").join(specifier)
};
if let Ok(path) = self.file_extension_probe(path, &referrer_path) {
return Ok(Some(
url_from_file_path(&path).map_err(JsErrorBox::from_err)?,
));
if let Ok(path) = self.file_extension_probe(path, referrer_path) {
return Ok(Some(UrlOrPath::Path(path)));
}
last = parent;
}
Err(not_found(specifier, &referrer_path))
Err(not_found(specifier, referrer_path))
}
fn file_extension_probe(

View file

@ -6,9 +6,11 @@ use std::path::PathBuf;
use boxed_error::Boxed;
use deno_error::JsError;
use deno_path_util::UrlToFilePathError;
use thiserror::Error;
use url::Url;
use crate::path::UrlOrPath;
use crate::NodeResolutionKind;
use crate::ResolutionMode;
@ -24,6 +26,7 @@ pub enum NodeJsErrorCode {
ERR_UNKNOWN_FILE_EXTENSION,
ERR_UNSUPPORTED_DIR_IMPORT,
ERR_UNSUPPORTED_ESM_URL_SCHEME,
ERR_INVALID_FILE_URL_PATH,
/// Deno specific since Node doesn't support TypeScript.
ERR_TYPES_NOT_FOUND,
}
@ -48,6 +51,7 @@ impl NodeJsErrorCode {
ERR_UNSUPPORTED_DIR_IMPORT => "ERR_UNSUPPORTED_DIR_IMPORT",
ERR_UNSUPPORTED_ESM_URL_SCHEME => "ERR_UNSUPPORTED_ESM_URL_SCHEME",
ERR_TYPES_NOT_FOUND => "ERR_TYPES_NOT_FOUND",
ERR_INVALID_FILE_URL_PATH => "ERR_INVALID_FILE_URL_PATH",
}
}
}
@ -109,7 +113,7 @@ impl NodeJsErrorCoded for LegacyResolveError {
#[class(generic)]
pub struct PackageNotFoundError {
pub package_name: String,
pub referrer: Url,
pub referrer: UrlOrPath,
/// Extra information about the referrer.
pub referrer_extra: Option<String>,
}
@ -128,7 +132,7 @@ impl NodeJsErrorCoded for PackageNotFoundError {
)]
#[class(generic)]
pub struct ReferrerNotFoundError {
pub referrer: Url,
pub referrer: UrlOrPath,
/// Extra information about the referrer.
pub referrer_extra: Option<String>,
}
@ -144,7 +148,7 @@ impl NodeJsErrorCoded for ReferrerNotFoundError {
#[error("Failed resolving '{package_name}' from referrer '{referrer}'.")]
pub struct PackageFolderResolveIoError {
pub package_name: String,
pub referrer: Url,
pub referrer: UrlOrPath,
#[source]
#[inherit]
pub source: std::io::Error,
@ -162,6 +166,9 @@ impl NodeJsErrorCoded for PackageFolderResolveError {
PackageFolderResolveErrorKind::PackageNotFound(e) => e.code(),
PackageFolderResolveErrorKind::ReferrerNotFound(e) => e.code(),
PackageFolderResolveErrorKind::Io(e) => e.code(),
PackageFolderResolveErrorKind::PathToUrl(_) => {
NodeJsErrorCode::ERR_INVALID_FILE_URL_PATH
}
}
}
}
@ -180,6 +187,9 @@ pub enum PackageFolderResolveErrorKind {
#[class(inherit)]
#[error(transparent)]
Io(#[from] PackageFolderResolveIoError),
#[class(inherit)]
#[error(transparent)]
PathToUrl(#[from] deno_path_util::PathToUrlError),
}
impl NodeJsErrorCoded for PackageSubpathResolveError {
@ -232,7 +242,7 @@ pub enum PackageSubpathResolveErrorKind {
pub struct PackageTargetNotFoundError {
pub pkg_json_path: PathBuf,
pub target: String,
pub maybe_referrer: Option<Url>,
pub maybe_referrer: Option<UrlOrPath>,
pub resolution_mode: ResolutionMode,
pub resolution_kind: NodeResolutionKind,
}
@ -251,6 +261,9 @@ impl NodeJsErrorCoded for PackageTargetResolveError {
PackageTargetResolveErrorKind::InvalidModuleSpecifier(e) => e.code(),
PackageTargetResolveErrorKind::PackageResolve(e) => e.code(),
PackageTargetResolveErrorKind::TypesNotFound(e) => e.code(),
PackageTargetResolveErrorKind::UrlToFilePath(_) => {
NodeJsErrorCode::ERR_INVALID_FILE_URL_PATH
}
}
}
}
@ -275,6 +288,9 @@ pub enum PackageTargetResolveErrorKind {
#[class(inherit)]
#[error(transparent)]
TypesNotFound(#[from] TypesNotFoundError),
#[class(inherit)]
#[error(transparent)]
UrlToFilePath(#[from] deno_path_util::UrlToFilePathError),
}
impl NodeJsErrorCoded for PackageExportsResolveError {
@ -311,8 +327,8 @@ pub struct TypesNotFoundError(pub Box<TypesNotFoundErrorData>);
#[derive(Debug)]
pub struct TypesNotFoundErrorData {
pub code_specifier: Url,
pub maybe_referrer: Option<Url>,
pub code_specifier: UrlOrPath,
pub maybe_referrer: Option<UrlOrPath>,
}
impl NodeJsErrorCoded for TypesNotFoundError {
@ -369,7 +385,7 @@ pub enum ClosestPkgJsonErrorKind {
pub struct PackageImportNotDefinedError {
pub name: String,
pub package_json_path: Option<PathBuf>,
pub maybe_referrer: Option<Url>,
pub maybe_referrer: Option<UrlOrPath>,
}
impl NodeJsErrorCoded for PackageImportNotDefinedError {
@ -416,6 +432,9 @@ impl NodeJsErrorCoded for PackageResolveError {
PackageResolveErrorKind::PackageFolderResolve(e) => e.code(),
PackageResolveErrorKind::ExportsResolve(e) => e.code(),
PackageResolveErrorKind::SubpathResolve(e) => e.code(),
PackageResolveErrorKind::UrlToFilePath(_) => {
NodeJsErrorCode::ERR_INVALID_FILE_URL_PATH
}
}
}
}
@ -440,6 +459,9 @@ pub enum PackageResolveErrorKind {
#[class(inherit)]
#[error(transparent)]
SubpathResolve(#[from] PackageSubpathResolveError),
#[class(inherit)]
#[error(transparent)]
UrlToFilePath(#[from] UrlToFilePathError),
}
#[derive(Debug, Error, JsError)]
@ -470,6 +492,12 @@ pub enum NodeResolveErrorKind {
RelativeJoin(#[from] NodeResolveRelativeJoinError),
#[class(inherit)]
#[error(transparent)]
PathToUrl(#[from] deno_path_util::PathToUrlError),
#[class(inherit)]
#[error(transparent)]
UrlToFilePath(#[from] deno_path_util::UrlToFilePathError),
#[class(inherit)]
#[error(transparent)]
PackageImportsResolve(#[from] PackageImportsResolveError),
#[class(inherit)]
#[error(transparent)]
@ -502,6 +530,9 @@ pub enum FinalizeResolutionErrorKind {
#[class(inherit)]
#[error(transparent)]
UnsupportedDirImport(#[from] UnsupportedDirImportError),
#[class(inherit)]
#[error(transparent)]
UrlToFilePath(#[from] deno_path_util::UrlToFilePathError),
}
impl NodeJsErrorCoded for FinalizeResolutionError {
@ -510,6 +541,9 @@ impl NodeJsErrorCoded for FinalizeResolutionError {
FinalizeResolutionErrorKind::InvalidModuleSpecifierError(e) => e.code(),
FinalizeResolutionErrorKind::ModuleNotFound(e) => e.code(),
FinalizeResolutionErrorKind::UnsupportedDirImport(e) => e.code(),
FinalizeResolutionErrorKind::UrlToFilePath(_) => {
NodeJsErrorCode::ERR_INVALID_FILE_URL_PATH
}
}
}
}
@ -524,8 +558,8 @@ impl NodeJsErrorCoded for FinalizeResolutionError {
maybe_referrer.as_ref().map(|referrer| format!(" imported from '{}'", referrer)).unwrap_or_default()
)]
pub struct ModuleNotFoundError {
pub specifier: Url,
pub maybe_referrer: Option<Url>,
pub specifier: UrlOrPath,
pub maybe_referrer: Option<UrlOrPath>,
pub typ: &'static str,
}
@ -544,8 +578,8 @@ impl NodeJsErrorCoded for ModuleNotFoundError {
maybe_referrer.as_ref().map(|referrer| format!(" imported from '{}'", referrer)).unwrap_or_default(),
)]
pub struct UnsupportedDirImportError {
pub dir_url: Url,
pub maybe_referrer: Option<Url>,
pub dir_url: UrlOrPath,
pub maybe_referrer: Option<UrlOrPath>,
}
impl NodeJsErrorCoded for UnsupportedDirImportError {
@ -561,7 +595,7 @@ pub struct InvalidPackageTargetError {
pub sub_path: String,
pub target: String,
pub is_import: bool,
pub maybe_referrer: Option<Url>,
pub maybe_referrer: Option<UrlOrPath>,
}
impl std::error::Error for InvalidPackageTargetError {}
@ -616,7 +650,7 @@ impl NodeJsErrorCoded for InvalidPackageTargetError {
pub struct PackagePathNotExportedError {
pub pkg_json_path: PathBuf,
pub subpath: String,
pub maybe_referrer: Option<Url>,
pub maybe_referrer: Option<UrlOrPath>,
pub resolution_kind: NodeResolutionKind,
}

View file

@ -24,6 +24,8 @@ pub use package_json::PackageJsonResolver;
pub use package_json::PackageJsonResolverRc;
pub use package_json::PackageJsonThreadLocalCache;
pub use path::PathClean;
pub use path::UrlOrPath;
pub use path::UrlOrPathRef;
pub use resolution::parse_npm_pkg_name;
pub use resolution::resolve_specifier_into_node_modules;
pub use resolution::ConditionsFromResolutionMode;

View file

@ -9,13 +9,14 @@ use url::Url;
use crate::errors;
use crate::path::PathClean;
use crate::path::UrlOrPathRef;
pub trait NpmPackageFolderResolver {
/// Resolves an npm package folder path from the specified referrer.
fn resolve_package_folder_from_package(
&self,
specifier: &str,
referrer: &Url,
referrer: &UrlOrPathRef,
) -> Result<PathBuf, errors::PackageFolderResolveError>;
}

View file

@ -9,7 +9,6 @@ use std::path::PathBuf;
use deno_package_json::PackageJson;
use deno_package_json::PackageJsonRc;
use sys_traits::FsRead;
use url::Url;
use crate::errors::ClosestPkgJsonError;
use crate::errors::PackageJsonLoadError;
@ -51,17 +50,17 @@ pub struct PackageJsonThreadLocalCache;
impl PackageJsonThreadLocalCache {
pub fn clear() {
CACHE.with(|cache| cache.borrow_mut().clear());
CACHE.with_borrow_mut(|cache| cache.clear());
}
}
impl deno_package_json::PackageJsonCache for PackageJsonThreadLocalCache {
fn get(&self, path: &Path) -> Option<PackageJsonRc> {
CACHE.with(|cache| cache.borrow().get(path).cloned())
CACHE.with_borrow(|cache| cache.get(path).cloned())
}
fn set(&self, path: PathBuf, package_json: PackageJsonRc) {
CACHE.with(|cache| cache.borrow_mut().insert(path, package_json));
CACHE.with_borrow_mut(|cache| cache.insert(path, package_json));
}
}
@ -81,20 +80,12 @@ impl<TSys: FsRead> PackageJsonResolver<TSys> {
}
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_file_path(&file_path)
}
pub fn get_closest_package_json_from_file_path(
&self,
file_path: &Path,
) -> Result<Option<PackageJsonRc>, ClosestPkgJsonError> {
let parent_dir = file_path.parent().unwrap();
let Some(parent_dir) = file_path.parent() else {
return Ok(None);
};
for current_dir in parent_dir.ancestors() {
let package_json_path = current_dir.join("package.json");
if let Some(pkg_json) = self.load_package_json(&package_json_path)? {

View file

@ -1,9 +1,125 @@
// Copyright 2018-2025 the Deno authors. MIT license.
use std::borrow::Cow;
use std::path::Component;
use std::path::Path;
use std::path::PathBuf;
use url::Url;
#[derive(Debug, Clone)]
pub enum UrlOrPath {
Url(Url),
Path(PathBuf),
}
impl UrlOrPath {
pub fn is_file(&self) -> bool {
match self {
UrlOrPath::Url(url) => url.scheme() == "file",
UrlOrPath::Path(_) => true,
}
}
pub fn is_node_url(&self) -> bool {
match self {
UrlOrPath::Url(url) => url.scheme() == "node",
UrlOrPath::Path(_) => false,
}
}
pub fn into_path(
self,
) -> Result<PathBuf, deno_path_util::UrlToFilePathError> {
match self {
UrlOrPath::Url(url) => deno_path_util::url_to_file_path(&url),
UrlOrPath::Path(path) => Ok(path),
}
}
pub fn into_url(self) -> Result<Url, deno_path_util::PathToUrlError> {
match self {
UrlOrPath::Url(url) => Ok(url),
UrlOrPath::Path(path) => deno_path_util::url_from_file_path(&path),
}
}
pub fn to_string_lossy(&self) -> Cow<str> {
match self {
UrlOrPath::Url(url) => Cow::Borrowed(url.as_str()),
UrlOrPath::Path(path) => path.to_string_lossy(),
}
}
}
impl std::fmt::Display for UrlOrPath {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
UrlOrPath::Url(url) => url.fmt(f),
UrlOrPath::Path(path) => {
// prefer displaying a url
match deno_path_util::url_from_file_path(path) {
Ok(url) => url.fmt(f),
Err(_) => {
write!(f, "{}", path.display())
}
}
}
}
}
}
pub struct UrlOrPathRef<'a> {
url: once_cell::unsync::OnceCell<Cow<'a, Url>>,
path: once_cell::unsync::OnceCell<Cow<'a, Path>>,
}
impl<'a> UrlOrPathRef<'a> {
pub fn from_path(path: &'a Path) -> Self {
Self {
url: Default::default(),
path: once_cell::unsync::OnceCell::with_value(Cow::Borrowed(path)),
}
}
pub fn from_url(url: &'a Url) -> Self {
Self {
path: Default::default(),
url: once_cell::unsync::OnceCell::with_value(Cow::Borrowed(url)),
}
}
pub fn url(&self) -> Result<&Url, deno_path_util::PathToUrlError> {
self
.url
.get_or_try_init(|| {
deno_path_util::url_from_file_path(self.path.get().unwrap())
.map(Cow::Owned)
})
.map(|v| v.as_ref())
}
pub fn path(&self) -> Result<&Path, deno_path_util::UrlToFilePathError> {
self
.path
.get_or_try_init(|| {
deno_path_util::url_to_file_path(self.url.get().unwrap())
.map(Cow::Owned)
})
.map(|v| v.as_ref())
}
pub fn display(&self) -> UrlOrPath {
// prefer url
if let Ok(url) = self.url() {
UrlOrPath::Url(url.clone())
} else {
// this will always be set if url is None
UrlOrPath::Path(self.path.get().unwrap().to_path_buf())
}
}
}
/// Extension to path_clean::PathClean
pub trait PathClean<T> {
fn clean(&self) -> T;

View file

@ -8,7 +8,6 @@ use std::path::PathBuf;
use anyhow::bail;
use anyhow::Error as AnyError;
use deno_package_json::PackageJson;
use deno_path_util::url_from_file_path;
use serde_json::Map;
use serde_json::Value;
use sys_traits::FileType;
@ -46,6 +45,8 @@ use crate::errors::TypesNotFoundError;
use crate::errors::TypesNotFoundErrorData;
use crate::errors::UnsupportedDirImportError;
use crate::errors::UnsupportedEsmUrlSchemeError;
use crate::path::UrlOrPath;
use crate::path::UrlOrPathRef;
use crate::InNpmPackageChecker;
use crate::IsBuiltInNodeModuleChecker;
use crate::NpmPackageFolderResolver;
@ -115,21 +116,19 @@ impl NodeResolutionKind {
#[derive(Debug)]
pub enum NodeResolution {
Module(Url),
Module(UrlOrPath),
BuiltIn(String),
}
impl NodeResolution {
pub fn into_url(self) -> Url {
pub fn into_url(self) -> Result<Url, NodeResolveError> {
match self {
Self::Module(u) => u,
Self::BuiltIn(specifier) => {
if specifier.starts_with("node:") {
Url::parse(&specifier).unwrap()
} else {
Url::parse(&format!("node:{specifier}")).unwrap()
}
}
Self::Module(u) => Ok(u.into_url()?),
Self::BuiltIn(specifier) => Ok(if specifier.starts_with("node:") {
Url::parse(&specifier).unwrap()
} else {
Url::parse(&format!("node:{specifier}")).unwrap()
}),
}
}
}
@ -220,7 +219,7 @@ impl<
if let Ok(url) = Url::parse(specifier) {
if url.scheme() == "data" {
return Ok(NodeResolution::Module(url));
return Ok(NodeResolution::Module(UrlOrPath::Url(url)));
}
if let Some(module_name) =
@ -245,26 +244,27 @@ impl<
let url = referrer
.join(specifier)
.map_err(|source| DataUrlReferrerError { source })?;
return Ok(NodeResolution::Module(url));
return Ok(NodeResolution::Module(UrlOrPath::Url(url)));
}
}
let conditions = self
.conditions_from_resolution_mode
.resolve(resolution_mode);
let referrer = UrlOrPathRef::from_url(referrer);
let url = self.module_resolve(
specifier,
referrer,
&referrer,
resolution_mode,
conditions,
resolution_kind,
)?;
let url = if resolution_kind.is_types() {
let file_path = to_file_path(&url);
self.path_to_declaration_url(
&file_path,
Some(referrer),
let url = if resolution_kind.is_types() && url.is_file() {
let file_path = url.into_path()?;
self.path_to_declaration_path(
file_path,
Some(&referrer),
resolution_mode,
conditions,
)?
@ -272,8 +272,8 @@ impl<
url
};
let url = self.finalize_resolution(url, Some(referrer))?;
let resolve_response = NodeResolution::Module(url);
let url_or_path = self.finalize_resolution(url, Some(&referrer))?;
let resolve_response = NodeResolution::Module(url_or_path);
// TODO(bartlomieju): skipped checking errors for commonJS resolution and
// "preserveSymlinksMain"/"preserveSymlinks" options.
Ok(resolve_response)
@ -282,23 +282,26 @@ impl<
fn module_resolve(
&self,
specifier: &str,
referrer: &Url,
referrer: &UrlOrPathRef,
resolution_mode: ResolutionMode,
conditions: &[&str],
resolution_kind: NodeResolutionKind,
) -> Result<Url, NodeResolveError> {
) -> Result<UrlOrPath, NodeResolveError> {
if should_be_treated_as_relative_or_absolute_path(specifier) {
Ok(node_join_url(referrer, specifier).map_err(|err| {
NodeResolveRelativeJoinError {
path: specifier.to_string(),
base: referrer.clone(),
source: err,
}
})?)
let referrer_url = referrer.url()?;
Ok(UrlOrPath::Url(
node_join_url(referrer_url, specifier).map_err(|err| {
NodeResolveRelativeJoinError {
path: specifier.to_string(),
base: referrer_url.clone(),
source: err,
}
})?,
))
} else if specifier.starts_with('#') {
let pkg_config = self
.pkg_json_resolver
.get_closest_package_json(referrer)
.get_closest_package_json(referrer.path()?)
.map_err(PackageImportsResolveErrorKind::ClosestPkgJson)
.map_err(|err| PackageImportsResolveError(Box::new(err)))?;
Ok(self.package_imports_resolve(
@ -310,7 +313,7 @@ impl<
resolution_kind,
)?)
} else if let Ok(resolved) = Url::parse(specifier) {
Ok(resolved)
Ok(UrlOrPath::Url(resolved))
} else {
Ok(self.package_resolve(
specifier,
@ -324,29 +327,37 @@ impl<
fn finalize_resolution(
&self,
resolved: Url,
maybe_referrer: Option<&Url>,
) -> Result<Url, FinalizeResolutionError> {
resolved: UrlOrPath,
maybe_referrer: Option<&UrlOrPathRef>,
) -> Result<UrlOrPath, FinalizeResolutionError> {
let encoded_sep_re = lazy_regex::regex!(r"%2F|%2C");
if encoded_sep_re.is_match(resolved.path()) {
let text = match &resolved {
UrlOrPath::Url(url) => Cow::Borrowed(url.as_str()),
UrlOrPath::Path(path_buf) => path_buf.to_string_lossy(),
};
if encoded_sep_re.is_match(&text) {
return Err(
errors::InvalidModuleSpecifierError {
request: resolved.to_string(),
reason: Cow::Borrowed(
"must not include encoded \"/\" or \"\\\\\" characters",
),
maybe_referrer: maybe_referrer.map(to_file_path_string),
maybe_referrer: maybe_referrer.map(|r| match r.path() {
// in this case, prefer showing the path string
Ok(path) => path.display().to_string(),
Err(_) => r.display().to_string(),
}),
}
.into(),
);
}
if resolved.scheme() == "node" {
if resolved.is_node_url() {
return Ok(resolved);
}
let path = to_file_path(&resolved);
let path = resolved.into_path()?;
// TODO(bartlomieju): currently not supported
// if (getOptionValue('--experimental-specifier-resolution') === 'node') {
@ -354,26 +365,27 @@ impl<
// }
let p_str = path.to_str().unwrap();
let p = if p_str.ends_with('/') {
p_str[p_str.len() - 1..].to_string()
let path = if p_str.ends_with('/') {
// todo(dsherret): don't allocate a new string here
PathBuf::from(p_str[p_str.len() - 1..].to_string())
} else {
p_str.to_string()
path
};
let maybe_file_type = self.sys.fs_metadata(p).map(|m| m.file_type());
let maybe_file_type = self.sys.fs_metadata(&path).map(|m| m.file_type());
match maybe_file_type {
Ok(FileType::Dir) => Err(
UnsupportedDirImportError {
dir_url: resolved.clone(),
maybe_referrer: maybe_referrer.map(ToOwned::to_owned),
dir_url: UrlOrPath::Path(path),
maybe_referrer: maybe_referrer.map(|r| r.display()),
}
.into(),
),
Ok(FileType::File) => Ok(resolved),
Ok(FileType::File) => Ok(UrlOrPath::Path(path)),
_ => Err(
ModuleNotFoundError {
specifier: resolved,
maybe_referrer: maybe_referrer.map(ToOwned::to_owned),
specifier: UrlOrPath::Path(path),
maybe_referrer: maybe_referrer.map(|r| r.display()),
typ: "module",
}
.into(),
@ -388,16 +400,17 @@ impl<
maybe_referrer: Option<&Url>,
resolution_mode: ResolutionMode,
resolution_kind: NodeResolutionKind,
) -> Result<Url, PackageSubpathResolveError> {
) -> Result<UrlOrPath, PackageSubpathResolveError> {
// todo(dsherret): don't allocate a string here (maybe use an
// enum that says the subpath is not prefixed with a ./)
let package_subpath = package_subpath
.map(|s| format!("./{s}"))
.unwrap_or_else(|| ".".to_string());
let maybe_referrer = maybe_referrer.map(UrlOrPathRef::from_url);
let resolved_url = self.resolve_package_dir_subpath(
package_dir,
&package_subpath,
maybe_referrer,
maybe_referrer.as_ref(),
resolution_mode,
self
.conditions_from_resolution_mode
@ -441,7 +454,7 @@ impl<
&self,
package_folder: &Path,
sub_path: Option<&str>,
) -> Result<Url, ResolvePkgJsonBinExportError> {
) -> Result<PathBuf, ResolvePkgJsonBinExportError> {
let pkg_json_path = package_folder.join("package.json");
let Some(package_json) =
self.pkg_json_resolver.load_package_json(&pkg_json_path)?
@ -456,18 +469,16 @@ impl<
message: err.to_string(),
}
})?;
let url = url_from_file_path(&package_folder.join(bin_entry)).unwrap();
// TODO(bartlomieju): skipped checking errors for commonJS resolution and
// "preserveSymlinksMain"/"preserveSymlinks" options.
Ok(url)
Ok(package_folder.join(bin_entry))
}
/// Resolves an npm package folder path from the specified referrer.
pub fn resolve_package_folder_from_package(
&self,
specifier: &str,
referrer: &Url,
referrer: &UrlOrPathRef,
) -> Result<PathBuf, errors::PackageFolderResolveError> {
self
.npm_pkg_folder_resolver
@ -475,13 +486,13 @@ impl<
}
/// Checks if the resolved file has a corresponding declaration file.
fn path_to_declaration_url(
fn path_to_declaration_path(
&self,
path: &Path,
maybe_referrer: Option<&Url>,
path: PathBuf,
maybe_referrer: Option<&UrlOrPathRef>,
resolution_mode: ResolutionMode,
conditions: &[&str],
) -> Result<Url, TypesNotFoundError> {
) -> Result<UrlOrPath, TypesNotFoundError> {
fn probe_extensions<TSys: FsMetadata>(
sys: &TSys,
path: &Path,
@ -492,20 +503,20 @@ impl<
let mut searched_for_d_cts = false;
if lowercase_path.ends_with(".mjs") {
let d_mts_path = with_known_extension(path, "d.mts");
if sys.fs_exists_no_err(&d_mts_path) {
if sys.fs_is_file_no_err(&d_mts_path) {
return Some(d_mts_path);
}
searched_for_d_mts = true;
} else if lowercase_path.ends_with(".cjs") {
let d_cts_path = with_known_extension(path, "d.cts");
if sys.fs_exists_no_err(&d_cts_path) {
if sys.fs_is_file_no_err(&d_cts_path) {
return Some(d_cts_path);
}
searched_for_d_cts = true;
}
let dts_path = with_known_extension(path, "d.ts");
if sys.fs_exists_no_err(&dts_path) {
if sys.fs_is_file_no_err(&dts_path) {
return Some(dts_path);
}
@ -519,7 +530,7 @@ impl<
_ => None, // already searched above
};
if let Some(specific_dts_path) = specific_dts_path {
if sys.fs_exists_no_err(&specific_dts_path) {
if sys.fs_is_file_no_err(&specific_dts_path) {
return Some(specific_dts_path);
}
}
@ -531,16 +542,16 @@ impl<
|| lowercase_path.ends_with(".d.cts")
|| lowercase_path.ends_with(".d.mts")
{
return Ok(url_from_file_path(path).unwrap());
return Ok(UrlOrPath::Path(path));
}
if let Some(path) =
probe_extensions(&self.sys, path, &lowercase_path, resolution_mode)
probe_extensions(&self.sys, &path, &lowercase_path, resolution_mode)
{
return Ok(url_from_file_path(&path).unwrap());
return Ok(UrlOrPath::Path(path));
}
if self.sys.fs_is_dir_no_err(path) {
if self.sys.fs_is_dir_no_err(&path) {
let resolution_result = self.resolve_package_dir_subpath(
path,
&path,
/* sub path */ ".",
maybe_referrer,
resolution_mode,
@ -557,16 +568,16 @@ impl<
&index_path.to_string_lossy().to_lowercase(),
resolution_mode,
) {
return Ok(url_from_file_path(&path).unwrap());
return Ok(UrlOrPath::Path(path));
}
}
// allow resolving .css files for types resolution
if lowercase_path.ends_with(".css") {
return Ok(url_from_file_path(path).unwrap());
return Ok(UrlOrPath::Path(path));
}
Err(TypesNotFoundError(Box::new(TypesNotFoundErrorData {
code_specifier: url_from_file_path(path).unwrap(),
maybe_referrer: maybe_referrer.cloned(),
code_specifier: UrlOrPathRef::from_path(&path).display(),
maybe_referrer: maybe_referrer.map(|r| r.display()),
})))
}
@ -574,12 +585,12 @@ impl<
pub fn package_imports_resolve(
&self,
name: &str,
maybe_referrer: Option<&Url>,
maybe_referrer: Option<&UrlOrPathRef>,
resolution_mode: ResolutionMode,
referrer_pkg_json: Option<&PackageJson>,
conditions: &[&str],
resolution_kind: NodeResolutionKind,
) -> Result<Url, PackageImportsResolveError> {
) -> Result<UrlOrPath, PackageImportsResolveError> {
if name == "#" || name.starts_with("#/") || name.ends_with('/') {
let reason = "is not a valid internal imports specifier name";
return Err(
@ -662,7 +673,7 @@ impl<
PackageImportNotDefinedError {
name: name.to_string(),
package_json_path,
maybe_referrer: maybe_referrer.map(ToOwned::to_owned),
maybe_referrer: maybe_referrer.map(|r| r.display()),
}
.into(),
)
@ -675,13 +686,13 @@ impl<
subpath: &str,
match_: &str,
package_json_path: &Path,
maybe_referrer: Option<&Url>,
maybe_referrer: Option<&UrlOrPathRef>,
resolution_mode: ResolutionMode,
pattern: bool,
internal: bool,
conditions: &[&str],
resolution_kind: NodeResolutionKind,
) -> Result<Url, PackageTargetResolveError> {
) -> Result<UrlOrPath, PackageTargetResolveError> {
if !subpath.is_empty() && !pattern && !target.ends_with('/') {
return Err(
InvalidPackageTargetError {
@ -689,7 +700,7 @@ impl<
sub_path: match_.to_string(),
target: target.to_string(),
is_import: internal,
maybe_referrer: maybe_referrer.map(ToOwned::to_owned),
maybe_referrer: maybe_referrer.map(|r| r.display()),
}
.into(),
);
@ -705,7 +716,7 @@ impl<
if get_module_name_from_builtin_node_module_specifier(&url)
.is_some()
{
return Ok(url);
return Ok(UrlOrPath::Url(url));
}
}
Err(_) => {
@ -716,18 +727,17 @@ impl<
} else {
format!("{target}{subpath}")
};
let package_json_url =
url_from_file_path(package_json_path).unwrap();
let result = match self.package_resolve(
&export_target,
&package_json_url,
&UrlOrPathRef::from_path(package_json_path),
resolution_mode,
conditions,
resolution_kind,
) {
Ok(url) => Ok(url),
Err(err) => match err.code() {
NodeJsErrorCode::ERR_INVALID_MODULE_SPECIFIER
NodeJsErrorCode::ERR_INVALID_FILE_URL_PATH
| NodeJsErrorCode::ERR_INVALID_MODULE_SPECIFIER
| NodeJsErrorCode::ERR_INVALID_PACKAGE_CONFIG
| NodeJsErrorCode::ERR_INVALID_PACKAGE_TARGET
| NodeJsErrorCode::ERR_PACKAGE_IMPORT_NOT_DEFINED
@ -743,7 +753,7 @@ impl<
PackageTargetNotFoundError {
pkg_json_path: package_json_path.to_path_buf(),
target: export_target.to_string(),
maybe_referrer: maybe_referrer.map(ToOwned::to_owned),
maybe_referrer: maybe_referrer.map(|r| r.display()),
resolution_mode,
resolution_kind,
},
@ -760,7 +770,9 @@ impl<
.is_built_in_node_module_checker
.is_builtin_node_module(target)
{
Ok(Url::parse(&format!("node:{}", target)).unwrap())
Ok(UrlOrPath::Url(
Url::parse(&format!("node:{}", target)).unwrap(),
))
} else {
Err(err)
}
@ -775,7 +787,7 @@ impl<
sub_path: match_.to_string(),
target: target.to_string(),
is_import: internal,
maybe_referrer: maybe_referrer.map(ToOwned::to_owned),
maybe_referrer: maybe_referrer.map(|r| r.display()),
}
.into(),
);
@ -787,7 +799,7 @@ impl<
sub_path: match_.to_string(),
target: target.to_string(),
is_import: internal,
maybe_referrer: maybe_referrer.map(ToOwned::to_owned),
maybe_referrer: maybe_referrer.map(|r| r.display()),
}
.into(),
);
@ -801,13 +813,13 @@ impl<
sub_path: match_.to_string(),
target: target.to_string(),
is_import: internal,
maybe_referrer: maybe_referrer.map(ToOwned::to_owned),
maybe_referrer: maybe_referrer.map(|r| r.display()),
}
.into(),
);
}
if subpath.is_empty() {
return Ok(url_from_file_path(&resolved_path).unwrap());
return Ok(UrlOrPath::Path(resolved_path));
}
if invalid_segment_re.is_match(subpath) {
let request = if pattern {
@ -829,11 +841,9 @@ impl<
let resolved_path_str = resolved_path.to_string_lossy();
let replaced = pattern_re
.replace(&resolved_path_str, |_caps: &regex::Captures| subpath);
return Ok(
url_from_file_path(&PathBuf::from(replaced.to_string())).unwrap(),
);
return Ok(UrlOrPath::Path(PathBuf::from(replaced.to_string())));
}
Ok(url_from_file_path(&resolved_path.join(subpath).clean()).unwrap())
Ok(UrlOrPath::Path(resolved_path.join(subpath).clean()))
}
#[allow(clippy::too_many_arguments)]
@ -843,13 +853,13 @@ impl<
target: &Value,
subpath: &str,
package_subpath: &str,
maybe_referrer: Option<&Url>,
maybe_referrer: Option<&UrlOrPathRef>,
resolution_mode: ResolutionMode,
pattern: bool,
internal: bool,
conditions: &[&str],
resolution_kind: NodeResolutionKind,
) -> Result<Option<Url>, PackageTargetResolveError> {
) -> Result<Option<UrlOrPath>, PackageTargetResolveError> {
let result = self.resolve_package_target_inner(
package_json_path,
target,
@ -899,15 +909,15 @@ impl<
target: &Value,
subpath: &str,
package_subpath: &str,
maybe_referrer: Option<&Url>,
maybe_referrer: Option<&UrlOrPathRef>,
resolution_mode: ResolutionMode,
pattern: bool,
internal: bool,
conditions: &[&str],
resolution_kind: NodeResolutionKind,
) -> Result<Option<Url>, PackageTargetResolveError> {
) -> Result<Option<UrlOrPath>, PackageTargetResolveError> {
if let Some(target) = target.as_str() {
let url = self.resolve_package_target_string(
let url_or_path = self.resolve_package_target_string(
target,
subpath,
package_subpath,
@ -919,16 +929,16 @@ impl<
conditions,
resolution_kind,
)?;
if resolution_kind.is_types() && url.scheme() == "file" {
let path = deno_path_util::url_to_file_path(&url).unwrap();
return Ok(Some(self.path_to_declaration_url(
&path,
if resolution_kind.is_types() && url_or_path.is_file() {
let path = url_or_path.into_path()?;
return Ok(Some(self.path_to_declaration_path(
path,
maybe_referrer,
resolution_mode,
conditions,
)?));
} else {
return Ok(Some(url));
return Ok(Some(url_or_path));
}
} else if let Some(target_arr) = target.as_array() {
if target_arr.is_empty() {
@ -1013,7 +1023,7 @@ impl<
sub_path: package_subpath.to_string(),
target: target.to_string(),
is_import: internal,
maybe_referrer: maybe_referrer.map(ToOwned::to_owned),
maybe_referrer: maybe_referrer.map(|r| r.display()),
}
.into(),
)
@ -1025,11 +1035,11 @@ impl<
package_json_path: &Path,
package_subpath: &str,
package_exports: &Map<String, Value>,
maybe_referrer: Option<&Url>,
maybe_referrer: Option<&UrlOrPathRef>,
resolution_mode: ResolutionMode,
conditions: &[&str],
resolution_kind: NodeResolutionKind,
) -> Result<Url, PackageExportsResolveError> {
) -> Result<UrlOrPath, PackageExportsResolveError> {
if let Some(target) = package_exports.get(package_subpath) {
if package_subpath.find('*').is_none() && !package_subpath.ends_with('/')
{
@ -1051,7 +1061,7 @@ impl<
PackagePathNotExportedError {
pkg_json_path: package_json_path.to_path_buf(),
subpath: package_subpath.to_string(),
maybe_referrer: maybe_referrer.map(ToOwned::to_owned),
maybe_referrer: maybe_referrer.map(|r| r.display()),
resolution_kind,
}
.into(),
@ -1116,7 +1126,7 @@ impl<
PackagePathNotExportedError {
pkg_json_path: package_json_path.to_path_buf(),
subpath: package_subpath.to_string(),
maybe_referrer: maybe_referrer.map(ToOwned::to_owned),
maybe_referrer: maybe_referrer.map(|r| r.display()),
resolution_kind,
}
.into(),
@ -1128,7 +1138,7 @@ impl<
PackagePathNotExportedError {
pkg_json_path: package_json_path.to_path_buf(),
subpath: package_subpath.to_string(),
maybe_referrer: maybe_referrer.map(ToOwned::to_owned),
maybe_referrer: maybe_referrer.map(|r| r.display()),
resolution_kind,
}
.into(),
@ -1138,16 +1148,17 @@ impl<
pub(super) fn package_resolve(
&self,
specifier: &str,
referrer: &Url,
referrer: &UrlOrPathRef,
resolution_mode: ResolutionMode,
conditions: &[&str],
resolution_kind: NodeResolutionKind,
) -> Result<Url, PackageResolveError> {
) -> Result<UrlOrPath, PackageResolveError> {
let (package_name, package_subpath, _is_scoped) =
parse_npm_pkg_name(specifier, referrer)?;
if let Some(package_config) =
self.pkg_json_resolver.get_closest_package_json(referrer)?
if let Some(package_config) = self
.pkg_json_resolver
.get_closest_package_json(referrer.path()?)?
{
// ResolveSelf
if package_config.name.as_deref() == Some(package_name) {
@ -1182,11 +1193,11 @@ impl<
&self,
package_name: &str,
package_subpath: &str,
referrer: &Url,
referrer: &UrlOrPathRef,
resolution_mode: ResolutionMode,
conditions: &[&str],
resolution_kind: NodeResolutionKind,
) -> Result<Url, PackageResolveError> {
) -> Result<UrlOrPath, PackageResolveError> {
let result = self.resolve_package_subpath_for_package_inner(
package_name,
package_subpath,
@ -1195,7 +1206,7 @@ impl<
conditions,
resolution_kind,
);
if resolution_kind.is_types() && !matches!(result, Ok(Url { .. })) {
if resolution_kind.is_types() && result.is_err() {
// try to resolve with the @types package
let package_name = types_package_name(package_name);
if let Ok(result) = self.resolve_package_subpath_for_package_inner(
@ -1217,11 +1228,11 @@ impl<
&self,
package_name: &str,
package_subpath: &str,
referrer: &Url,
referrer: &UrlOrPathRef,
resolution_mode: ResolutionMode,
conditions: &[&str],
resolution_kind: NodeResolutionKind,
) -> Result<Url, PackageResolveError> {
) -> Result<UrlOrPath, PackageResolveError> {
let package_dir_path = self
.npm_pkg_folder_resolver
.resolve_package_folder_from_package(package_name, referrer)?;
@ -1257,11 +1268,11 @@ impl<
&self,
package_dir_path: &Path,
package_subpath: &str,
maybe_referrer: Option<&Url>,
maybe_referrer: Option<&UrlOrPathRef>,
resolution_mode: ResolutionMode,
conditions: &[&str],
resolution_kind: NodeResolutionKind,
) -> Result<Url, PackageSubpathResolveError> {
) -> Result<UrlOrPath, PackageSubpathResolveError> {
let package_json_path = package_dir_path.join("package.json");
match self
.pkg_json_resolver
@ -1295,11 +1306,11 @@ impl<
&self,
package_json: &PackageJson,
package_subpath: &str,
referrer: Option<&Url>,
referrer: Option<&UrlOrPathRef>,
resolution_mode: ResolutionMode,
conditions: &[&str],
resolution_kind: NodeResolutionKind,
) -> Result<Url, PackageSubpathResolveError> {
) -> Result<UrlOrPath, PackageSubpathResolveError> {
if let Some(exports) = &package_json.exports {
let result = self.package_exports_resolve(
&package_json.path,
@ -1365,22 +1376,22 @@ impl<
&self,
directory: &Path,
package_subpath: &str,
referrer: Option<&Url>,
referrer: Option<&UrlOrPathRef>,
resolution_mode: ResolutionMode,
conditions: &[&str],
resolution_kind: NodeResolutionKind,
) -> Result<Url, TypesNotFoundError> {
) -> Result<UrlOrPath, TypesNotFoundError> {
assert_ne!(package_subpath, ".");
let file_path = directory.join(package_subpath);
if resolution_kind.is_types() {
Ok(self.path_to_declaration_url(
&file_path,
Ok(self.path_to_declaration_path(
file_path,
referrer,
resolution_mode,
conditions,
)?)
} else {
Ok(url_from_file_path(&file_path).unwrap())
Ok(UrlOrPath::Path(file_path))
}
}
@ -1388,18 +1399,20 @@ impl<
&self,
directory: &Path,
package_subpath: &str,
maybe_referrer: Option<&Url>,
maybe_referrer: Option<&UrlOrPathRef>,
resolution_mode: ResolutionMode,
conditions: &[&str],
resolution_kind: NodeResolutionKind,
) -> Result<Url, LegacyResolveError> {
) -> Result<UrlOrPath, LegacyResolveError> {
if package_subpath == "." {
self.legacy_index_resolve(
directory,
maybe_referrer,
resolution_mode,
resolution_kind,
)
self
.legacy_index_resolve(
directory,
maybe_referrer,
resolution_mode,
resolution_kind,
)
.map(UrlOrPath::Path)
} else {
self
.resolve_subpath_exact(
@ -1417,11 +1430,11 @@ impl<
pub(super) fn legacy_main_resolve(
&self,
package_json: &PackageJson,
maybe_referrer: Option<&Url>,
maybe_referrer: Option<&UrlOrPathRef>,
resolution_mode: ResolutionMode,
conditions: &[&str],
resolution_kind: NodeResolutionKind,
) -> Result<Url, LegacyResolveError> {
) -> Result<UrlOrPath, LegacyResolveError> {
let pkg_json_kind = match resolution_mode {
ResolutionMode::Require => deno_package_json::NodeModuleKind::Cjs,
ResolutionMode::Import => deno_package_json::NodeModuleKind::Esm,
@ -1434,15 +1447,15 @@ impl<
// a corresponding declaration file
if let Some(main) = package_json.main(pkg_json_kind) {
let main = package_json.path.parent().unwrap().join(main).clean();
let decl_url_result = self.path_to_declaration_url(
&main,
let decl_path_result = self.path_to_declaration_path(
main,
maybe_referrer,
resolution_mode,
conditions,
);
// don't surface errors, fallback to checking the index now
if let Ok(url) = decl_url_result {
return Ok(url);
if let Ok(url_or_path) = decl_path_result {
return Ok(url_or_path);
}
}
None
@ -1455,7 +1468,7 @@ impl<
if let Some(main) = maybe_main {
let guess = package_json.path.parent().unwrap().join(main).clean();
if self.sys.fs_is_file_no_err(&guess) {
return Ok(url_from_file_path(&guess).unwrap());
return Ok(UrlOrPath::Path(guess));
}
// todo(dsherret): investigate exactly how node and typescript handles this
@ -1485,26 +1498,28 @@ impl<
.clean();
if self.sys.fs_is_file_no_err(&guess) {
// TODO(bartlomieju): emitLegacyIndexDeprecation()
return Ok(url_from_file_path(&guess).unwrap());
return Ok(UrlOrPath::Path(guess));
}
}
}
self.legacy_index_resolve(
package_json.path.parent().unwrap(),
maybe_referrer,
resolution_mode,
resolution_kind,
)
self
.legacy_index_resolve(
package_json.path.parent().unwrap(),
maybe_referrer,
resolution_mode,
resolution_kind,
)
.map(UrlOrPath::Path)
}
fn legacy_index_resolve(
&self,
directory: &Path,
maybe_referrer: Option<&Url>,
maybe_referrer: Option<&UrlOrPathRef>,
resolution_mode: ResolutionMode,
resolution_kind: NodeResolutionKind,
) -> Result<Url, LegacyResolveError> {
) -> Result<PathBuf, LegacyResolveError> {
let index_file_names = if resolution_kind.is_types() {
// todo(dsherret): investigate exactly how typescript does this
match resolution_mode {
@ -1520,25 +1535,25 @@ impl<
let guess = directory.join(index_file_name).clean();
if self.sys.fs_is_file_no_err(&guess) {
// TODO(bartlomieju): emitLegacyIndexDeprecation()
return Ok(url_from_file_path(&guess).unwrap());
return Ok(guess);
}
}
if resolution_kind.is_types() {
Err(
TypesNotFoundError(Box::new(TypesNotFoundErrorData {
code_specifier: url_from_file_path(&directory.join("index.js"))
.unwrap(),
maybe_referrer: maybe_referrer.cloned(),
code_specifier: UrlOrPathRef::from_path(&directory.join("index.js"))
.display(),
maybe_referrer: maybe_referrer.map(|r| r.display()),
}))
.into(),
)
} else {
Err(
ModuleNotFoundError {
specifier: url_from_file_path(&directory.join("index.js")).unwrap(),
specifier: UrlOrPath::Path(directory.join("index.js")),
typ: "module",
maybe_referrer: maybe_referrer.cloned(),
maybe_referrer: maybe_referrer.map(|r| r.display()),
}
.into(),
)
@ -1652,14 +1667,6 @@ fn resolve_bin_entry_value<'a>(
}
}
fn to_file_path(url: &Url) -> PathBuf {
deno_path_util::url_to_file_path(url).unwrap()
}
fn to_file_path_string(url: &Url) -> String {
to_file_path(url).display().to_string()
}
fn should_be_treated_as_relative_or_absolute_path(specifier: &str) -> bool {
if specifier.is_empty() {
return false;
@ -1731,11 +1738,11 @@ fn with_known_extension(path: &Path, ext: &str) -> PathBuf {
path.with_file_name(format!("{file_name}.{ext}"))
}
fn to_specifier_display_string(url: &Url) -> String {
if let Ok(path) = deno_path_util::url_to_file_path(url) {
fn to_specifier_display_string(url: &UrlOrPathRef) -> String {
if let Ok(path) = url.path() {
path.display().to_string()
} else {
url.to_string()
url.display().to_string()
}
}
@ -1743,7 +1750,7 @@ fn throw_invalid_subpath(
subpath: String,
package_json_path: &Path,
internal: bool,
maybe_referrer: Option<&Url>,
maybe_referrer: Option<&UrlOrPathRef>,
) -> InvalidModuleSpecifierError {
let ie = if internal { "imports" } else { "exports" };
let reason = format!(
@ -1760,7 +1767,7 @@ fn throw_invalid_subpath(
pub fn parse_npm_pkg_name<'a>(
specifier: &'a str,
referrer: &Url,
referrer: &UrlOrPathRef,
) -> Result<(&'a str, Cow<'static, str>, bool), InvalidModuleSpecifierError> {
let mut separator_index = specifier.find('/');
let mut valid_package_name = true;
@ -2045,6 +2052,7 @@ mod tests {
#[test]
fn test_parse_package_name() {
let dummy_referrer = Url::parse("http://example.com").unwrap();
let dummy_referrer = UrlOrPathRef::from_url(&dummy_referrer);
assert_eq!(
parse_npm_pkg_name("fetch-blob", &dummy_referrer).unwrap(),