summaryrefslogtreecommitdiff
path: root/ext/node
diff options
context:
space:
mode:
Diffstat (limited to 'ext/node')
-rw-r--r--ext/node/02_require.js6
-rw-r--r--ext/node/Cargo.toml1
-rw-r--r--ext/node/errors.rs10
-rw-r--r--ext/node/lib.rs44
-rw-r--r--ext/node/package_json.rs25
-rw-r--r--ext/node/resolution.rs331
6 files changed, 269 insertions, 148 deletions
diff --git a/ext/node/02_require.js b/ext/node/02_require.js
index 99587472a..4f2a998b2 100644
--- a/ext/node/02_require.js
+++ b/ext/node/02_require.js
@@ -390,8 +390,8 @@
return false;
};
- Module._nodeModulePaths = function (from) {
- return ops.op_require_node_module_paths(from);
+ Module._nodeModulePaths = function (fromPath) {
+ return ops.op_require_node_module_paths(fromPath);
};
Module._resolveLookupPaths = function (request, parent) {
@@ -728,7 +728,7 @@
const content = ops.op_require_read_file(filename);
if (StringPrototypeEndsWith(filename, ".js")) {
- const pkg = core.ops.op_require_read_package_scope(filename);
+ const pkg = core.ops.op_require_read_closest_package_json(filename);
if (pkg && pkg.exists && pkg.typ == "module") {
let message = `Trying to import ESM module: ${filename}`;
diff --git a/ext/node/Cargo.toml b/ext/node/Cargo.toml
index 2b72309d8..2391fbed0 100644
--- a/ext/node/Cargo.toml
+++ b/ext/node/Cargo.toml
@@ -15,5 +15,6 @@ path = "lib.rs"
[dependencies]
deno_core = { version = "0.148.0", path = "../../core" }
+path-clean = "=0.1.0"
regex = "1"
serde = "1.0.136"
diff --git a/ext/node/errors.rs b/ext/node/errors.rs
index 8d1822f7b..9dc6c7e7e 100644
--- a/ext/node/errors.rs
+++ b/ext/node/errors.rs
@@ -56,7 +56,7 @@ pub fn err_invalid_package_target(
key: String,
target: String,
is_import: bool,
- maybe_base: Option<String>,
+ maybe_referrer: Option<String>,
) -> AnyError {
let rel_error = !is_import && !target.is_empty() && !target.starts_with("./");
let mut msg = "[ERR_INVALID_PACKAGE_TARGET]".to_string();
@@ -69,7 +69,7 @@ pub fn err_invalid_package_target(
msg = format!("{} Invalid \"{}\" target {} defined for '{}' in the package config {}package.json", msg, ie, target, key, pkg_path)
};
- if let Some(base) = maybe_base {
+ if let Some(base) = maybe_referrer {
msg = format!("{} imported from {}", msg, base);
};
if rel_error {
@@ -82,7 +82,7 @@ pub fn err_invalid_package_target(
pub fn err_package_path_not_exported(
pkg_path: String,
subpath: String,
- maybe_base: Option<String>,
+ maybe_referrer: Option<String>,
) -> AnyError {
let mut msg = "[ERR_PACKAGE_PATH_NOT_EXPORTED]".to_string();
@@ -95,8 +95,8 @@ pub fn err_package_path_not_exported(
msg = format!("{} Package subpath \'{}\' is not defined by \"exports\" in {}package.json", msg, subpath, pkg_path);
};
- if let Some(base) = maybe_base {
- msg = format!("{} imported from {}", msg, base);
+ if let Some(referrer) = maybe_referrer {
+ msg = format!("{} imported from {}", msg, referrer);
}
generic_error(msg)
diff --git a/ext/node/lib.rs b/ext/node/lib.rs
index f96259797..6f3173470 100644
--- a/ext/node/lib.rs
+++ b/ext/node/lib.rs
@@ -12,11 +12,13 @@ use std::path::PathBuf;
use std::rc::Rc;
pub use package_json::PackageJson;
+pub use resolution::get_closest_package_json;
pub use resolution::get_package_scope_config;
pub use resolution::legacy_main_resolve;
pub use resolution::package_exports_resolve;
pub use resolution::package_imports_resolve;
pub use resolution::package_resolve;
+pub use resolution::NodeModuleKind;
pub use resolution::DEFAULT_CONDITIONS;
pub trait NodePermissions {
@@ -77,6 +79,7 @@ pub fn init<P: NodePermissions + 'static>(
op_require_read_file::decl::<P>(),
op_require_as_file_path::decl(),
op_require_resolve_exports::decl(),
+ op_require_read_closest_package_json::decl::<P>(),
op_require_read_package_scope::decl(),
op_require_package_imports_resolve::decl::<P>(),
])
@@ -485,17 +488,18 @@ fn op_require_try_self(
return Ok(None);
}
- let base = deno_core::url::Url::from_file_path(PathBuf::from("/")).unwrap();
+ let referrer = deno_core::url::Url::from_file_path(&pkg.path).unwrap();
if let Some(exports) = &pkg.exports {
resolution::package_exports_resolve(
- deno_core::url::Url::from_file_path(&pkg.path).unwrap(),
+ &pkg.path,
expansion,
exports,
- &base,
+ &referrer,
+ NodeModuleKind::Cjs,
resolution::REQUIRE_CONDITIONS,
&*resolver,
)
- .map(|r| Some(r.as_str().to_string()))
+ .map(|r| Some(r.to_string_lossy().to_string()))
} else {
Ok(None)
}
@@ -550,22 +554,43 @@ fn op_require_resolve_exports(
)?;
if let Some(exports) = &pkg.exports {
- let base = Url::from_file_path(parent_path).unwrap();
+ let referrer = Url::from_file_path(parent_path).unwrap();
resolution::package_exports_resolve(
- deno_core::url::Url::from_directory_path(pkg_path).unwrap(),
+ &pkg.path,
format!(".{}", expansion),
exports,
- &base,
+ &referrer,
+ NodeModuleKind::Cjs,
resolution::REQUIRE_CONDITIONS,
&*resolver,
)
- .map(|r| Some(r.to_file_path().unwrap().to_string_lossy().to_string()))
+ .map(|r| Some(r.to_string_lossy().to_string()))
} else {
Ok(None)
}
}
#[op]
+fn op_require_read_closest_package_json<P>(
+ state: &mut OpState,
+ filename: String,
+) -> Result<PackageJson, AnyError>
+where
+ P: NodePermissions + 'static,
+{
+ check_unstable(state);
+ ensure_read_permission::<P>(
+ state,
+ PathBuf::from(&filename).parent().unwrap(),
+ )?;
+ let resolver = state.borrow::<Rc<dyn DenoDirNpmResolver>>().clone();
+ resolution::get_closest_package_json(
+ &Url::from_file_path(filename).unwrap(),
+ &*resolver,
+ )
+}
+
+#[op]
fn op_require_read_package_scope(
state: &mut OpState,
filename: String,
@@ -600,10 +625,11 @@ where
let r = resolution::package_imports_resolve(
&request,
&referrer,
+ NodeModuleKind::Cjs,
resolution::REQUIRE_CONDITIONS,
&*resolver,
)
- .map(|r| Some(r.as_str().to_string()));
+ .map(|r| Some(Url::from_file_path(r).unwrap().to_string()));
state.put(resolver);
r
} else {
diff --git a/ext/node/package_json.rs b/ext/node/package_json.rs
index 19a79da96..15b5ab920 100644
--- a/ext/node/package_json.rs
+++ b/ext/node/package_json.rs
@@ -19,6 +19,7 @@ pub struct PackageJson {
pub imports: Option<Map<String, Value>>,
pub bin: Option<Value>,
pub main: Option<String>,
+ pub module: Option<String>,
pub name: Option<String>,
pub path: PathBuf,
pub typ: String,
@@ -33,6 +34,7 @@ impl PackageJson {
imports: None,
bin: None,
main: None,
+ module: None,
name: None,
path,
typ: "none".to_string(),
@@ -66,6 +68,7 @@ impl PackageJson {
let imports_val = package_json.get("imports");
let main_val = package_json.get("main");
+ let module_val = package_json.get("module");
let name_val = package_json.get("name");
let type_val = package_json.get("type");
let bin = package_json.get("bin").map(ToOwned::to_owned);
@@ -79,21 +82,12 @@ impl PackageJson {
}
});
- let imports = if let Some(imp) = imports_val {
- imp.as_object().map(|imp| imp.to_owned())
- } else {
- None
- };
- let main = if let Some(m) = main_val {
- m.as_str().map(|m| m.to_string())
- } else {
- None
- };
- let name = if let Some(n) = name_val {
- n.as_str().map(|n| n.to_string())
- } else {
- None
- };
+ let imports = imports_val
+ .and_then(|imp| imp.as_object())
+ .map(|imp| imp.to_owned());
+ let main = main_val.and_then(|s| s.as_str()).map(|s| s.to_string());
+ let name = name_val.and_then(|s| s.as_str()).map(|s| s.to_string());
+ let module = module_val.and_then(|s| s.as_str()).map(|s| s.to_string());
// Ignore unknown types for forwards compatibility
let typ = if let Some(t) = type_val {
@@ -121,6 +115,7 @@ impl PackageJson {
path,
main,
name,
+ module,
typ,
types,
exports,
diff --git a/ext/node/resolution.rs b/ext/node/resolution.rs
index b77e6f690..21ce589c2 100644
--- a/ext/node/resolution.rs
+++ b/ext/node/resolution.rs
@@ -1,13 +1,16 @@
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
+use std::path::Path;
use std::path::PathBuf;
+use deno_core::anyhow::bail;
use deno_core::error::generic_error;
use deno_core::error::AnyError;
use deno_core::serde_json::Map;
use deno_core::serde_json::Value;
use deno_core::url::Url;
use deno_core::ModuleSpecifier;
+use path_clean::PathClean;
use regex::Regex;
use crate::errors;
@@ -17,25 +20,29 @@ use crate::DenoDirNpmResolver;
pub static DEFAULT_CONDITIONS: &[&str] = &["deno", "node", "import"];
pub static REQUIRE_CONDITIONS: &[&str] = &["require", "node"];
-fn to_file_path(url: &ModuleSpecifier) -> PathBuf {
- url
- .to_file_path()
- .unwrap_or_else(|_| panic!("Provided URL was not file:// URL: {}", url))
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+pub enum NodeModuleKind {
+ Esm,
+ Cjs,
}
-fn to_file_path_string(url: &ModuleSpecifier) -> String {
- to_file_path(url).display().to_string()
+fn to_specifier_display_string(url: &ModuleSpecifier) -> String {
+ if let Ok(path) = url.to_file_path() {
+ path.display().to_string()
+ } else {
+ url.to_string()
+ }
}
fn throw_import_not_defined(
specifier: &str,
- package_json_url: Option<ModuleSpecifier>,
+ package_json_path: Option<&Path>,
base: &ModuleSpecifier,
) -> AnyError {
errors::err_package_import_not_defined(
specifier,
- package_json_url.map(|u| to_file_path_string(&u.join(".").unwrap())),
- &to_file_path_string(base),
+ package_json_path.map(|p| p.parent().unwrap().display().to_string()),
+ &to_specifier_display_string(base),
)
}
@@ -84,30 +91,32 @@ fn pattern_key_compare(a: &str, b: &str) -> i32 {
pub fn package_imports_resolve(
name: &str,
referrer: &ModuleSpecifier,
+ referrer_kind: NodeModuleKind,
conditions: &[&str],
npm_resolver: &dyn DenoDirNpmResolver,
-) -> Result<ModuleSpecifier, AnyError> {
+) -> Result<PathBuf, AnyError> {
if name == "#" || name.starts_with("#/") || name.ends_with('/') {
let reason = "is not a valid internal imports specifier name";
return Err(errors::err_invalid_module_specifier(
name,
reason,
- Some(to_file_path_string(referrer)),
+ Some(to_specifier_display_string(referrer)),
));
}
let package_config = get_package_scope_config(referrer, npm_resolver)?;
- let mut package_json_url = None;
+ let mut package_json_path = None;
if package_config.exists {
- package_json_url = Some(Url::from_file_path(package_config.path).unwrap());
+ package_json_path = Some(package_config.path.clone());
if let Some(imports) = &package_config.imports {
if imports.contains_key(name) && !name.contains('*') {
let maybe_resolved = resolve_package_target(
- package_json_url.clone().unwrap(),
+ package_json_path.as_ref().unwrap(),
imports.get(name).unwrap().to_owned(),
"".to_string(),
name.to_string(),
referrer,
+ referrer_kind,
false,
true,
conditions,
@@ -143,11 +152,12 @@ pub fn package_imports_resolve(
if !best_match.is_empty() {
let target = imports.get(best_match).unwrap().to_owned();
let maybe_resolved = resolve_package_target(
- package_json_url.clone().unwrap(),
+ package_json_path.as_ref().unwrap(),
target,
best_match_subpath.unwrap(),
best_match.to_string(),
referrer,
+ referrer_kind,
true,
true,
conditions,
@@ -161,41 +171,45 @@ pub fn package_imports_resolve(
}
}
- Err(throw_import_not_defined(name, package_json_url, referrer))
+ Err(throw_import_not_defined(
+ name,
+ package_json_path.as_deref(),
+ referrer,
+ ))
}
fn throw_invalid_package_target(
subpath: String,
target: String,
- package_json_url: &ModuleSpecifier,
+ package_json_path: &Path,
internal: bool,
- base: &ModuleSpecifier,
+ referrer: &ModuleSpecifier,
) -> AnyError {
errors::err_invalid_package_target(
- to_file_path_string(&package_json_url.join(".").unwrap()),
+ package_json_path.parent().unwrap().display().to_string(),
subpath,
target,
internal,
- Some(base.as_str().to_string()),
+ Some(referrer.as_str().to_string()),
)
}
fn throw_invalid_subpath(
subpath: String,
- package_json_url: &ModuleSpecifier,
+ package_json_path: &Path,
internal: bool,
- base: &ModuleSpecifier,
+ referrer: &ModuleSpecifier,
) -> AnyError {
let ie = if internal { "imports" } else { "exports" };
let reason = format!(
"request is not a valid subpath for the \"{}\" resolution of {}",
ie,
- to_file_path_string(package_json_url)
+ package_json_path.display(),
);
errors::err_invalid_module_specifier(
&subpath,
&reason,
- Some(to_file_path_string(base)),
+ Some(to_specifier_display_string(referrer)),
)
}
@@ -204,20 +218,21 @@ fn resolve_package_target_string(
target: String,
subpath: String,
match_: String,
- package_json_url: ModuleSpecifier,
- base: &ModuleSpecifier,
+ package_json_path: &Path,
+ referrer: &ModuleSpecifier,
+ referrer_kind: NodeModuleKind,
pattern: bool,
internal: bool,
conditions: &[&str],
npm_resolver: &dyn DenoDirNpmResolver,
-) -> Result<ModuleSpecifier, AnyError> {
+) -> Result<PathBuf, AnyError> {
if !subpath.is_empty() && !pattern && !target.ends_with('/') {
return Err(throw_invalid_package_target(
match_,
target,
- &package_json_url,
+ package_json_path,
internal,
- base,
+ referrer,
));
}
let invalid_segment_re =
@@ -234,9 +249,12 @@ fn resolve_package_target_string(
} else {
format!("{}{}", target, subpath)
};
+ let package_json_url =
+ ModuleSpecifier::from_file_path(package_json_path).unwrap();
return package_resolve(
&export_target,
&package_json_url,
+ referrer_kind,
conditions,
npm_resolver,
);
@@ -245,35 +263,33 @@ fn resolve_package_target_string(
return Err(throw_invalid_package_target(
match_,
target,
- &package_json_url,
+ package_json_path,
internal,
- base,
+ referrer,
));
}
if invalid_segment_re.is_match(&target[2..]) {
return Err(throw_invalid_package_target(
match_,
target,
- &package_json_url,
+ package_json_path,
internal,
- base,
+ referrer,
));
}
- let resolved = package_json_url.join(&target)?;
- let resolved_path = resolved.path();
- let package_url = package_json_url.join(".").unwrap();
- let package_path = package_url.path();
+ let package_path = package_json_path.parent().unwrap();
+ let resolved_path = package_path.join(&target).clean();
if !resolved_path.starts_with(package_path) {
return Err(throw_invalid_package_target(
match_,
target,
- &package_json_url,
+ package_json_path,
internal,
- base,
+ referrer,
));
}
if subpath.is_empty() {
- return Ok(resolved);
+ return Ok(resolved_path);
}
if invalid_segment_re.is_match(&subpath) {
let request = if pattern {
@@ -283,39 +299,43 @@ fn resolve_package_target_string(
};
return Err(throw_invalid_subpath(
request,
- &package_json_url,
+ package_json_path,
internal,
- base,
+ referrer,
));
}
if pattern {
+ let resolved_path_str = resolved_path.to_string_lossy();
let replaced = pattern_re
- .replace(resolved.as_str(), |_caps: &regex::Captures| subpath.clone());
- let url = Url::parse(&replaced)?;
- return Ok(url);
+ .replace(&resolved_path_str, |_caps: &regex::Captures| {
+ subpath.clone()
+ });
+ return Ok(PathBuf::from(replaced.to_string()));
}
- Ok(resolved.join(&subpath)?)
+ Ok(resolved_path.join(&subpath).clean())
}
#[allow(clippy::too_many_arguments)]
fn resolve_package_target(
- package_json_url: ModuleSpecifier,
+ package_json_path: &Path,
target: Value,
subpath: String,
package_subpath: String,
- base: &ModuleSpecifier,
+ referrer: &ModuleSpecifier,
+ referrer_kind: NodeModuleKind,
pattern: bool,
internal: bool,
conditions: &[&str],
npm_resolver: &dyn DenoDirNpmResolver,
-) -> Result<Option<ModuleSpecifier>, AnyError> {
+) -> Result<Option<PathBuf>, AnyError> {
if let Some(target) = target.as_str() {
return Ok(Some(resolve_package_target_string(
target.to_string(),
subpath,
package_subpath,
- package_json_url,
- base,
+ package_json_path,
+ referrer,
+ referrer_kind,
pattern,
internal,
conditions,
@@ -329,11 +349,12 @@ fn resolve_package_target(
let mut last_error = None;
for target_item in target_arr {
let resolved_result = resolve_package_target(
- package_json_url.clone(),
+ package_json_path,
target_item.to_owned(),
subpath.clone(),
package_subpath.clone(),
- base,
+ referrer,
+ referrer_kind,
pattern,
internal,
conditions,
@@ -371,11 +392,12 @@ fn resolve_package_target(
if key == "default" || conditions.contains(&key.as_str()) {
let condition_target = target_obj.get(key).unwrap().to_owned();
let resolved = resolve_package_target(
- package_json_url.clone(),
+ package_json_path,
condition_target,
subpath.clone(),
package_subpath.clone(),
- base,
+ referrer,
+ referrer_kind,
pattern,
internal,
conditions,
@@ -394,43 +416,45 @@ fn resolve_package_target(
Err(throw_invalid_package_target(
package_subpath,
target.to_string(),
- &package_json_url,
+ package_json_path,
internal,
- base,
+ referrer,
))
}
fn throw_exports_not_found(
subpath: String,
- package_json_url: &ModuleSpecifier,
- base: &ModuleSpecifier,
+ package_json_path: &Path,
+ referrer: &ModuleSpecifier,
) -> AnyError {
errors::err_package_path_not_exported(
- to_file_path_string(&package_json_url.join(".").unwrap()),
+ package_json_path.parent().unwrap().display().to_string(),
subpath,
- Some(to_file_path_string(base)),
+ Some(to_specifier_display_string(referrer)),
)
}
pub fn package_exports_resolve(
- package_json_url: ModuleSpecifier,
+ package_json_path: &Path,
package_subpath: String,
package_exports: &Map<String, Value>,
- base: &ModuleSpecifier,
+ referrer: &ModuleSpecifier,
+ referrer_kind: NodeModuleKind,
conditions: &[&str],
npm_resolver: &dyn DenoDirNpmResolver,
-) -> Result<ModuleSpecifier, AnyError> {
+) -> Result<PathBuf, AnyError> {
if package_exports.contains_key(&package_subpath)
&& package_subpath.find('*').is_none()
&& !package_subpath.ends_with('/')
{
let target = package_exports.get(&package_subpath).unwrap().to_owned();
let resolved = resolve_package_target(
- package_json_url.clone(),
+ package_json_path,
target,
"".to_string(),
package_subpath.to_string(),
- base,
+ referrer,
+ referrer_kind,
false,
false,
conditions,
@@ -439,8 +463,8 @@ pub fn package_exports_resolve(
if resolved.is_none() {
return Err(throw_exports_not_found(
package_subpath,
- &package_json_url,
- base,
+ package_json_path,
+ referrer,
));
}
return Ok(resolved.unwrap());
@@ -483,11 +507,12 @@ pub fn package_exports_resolve(
if !best_match.is_empty() {
let target = package_exports.get(best_match).unwrap().to_owned();
let maybe_resolved = resolve_package_target(
- package_json_url.clone(),
+ package_json_path,
target,
best_match_subpath.unwrap(),
best_match.to_string(),
- base,
+ referrer,
+ referrer_kind,
true,
false,
conditions,
@@ -498,22 +523,22 @@ pub fn package_exports_resolve(
} else {
return Err(throw_exports_not_found(
package_subpath,
- &package_json_url,
- base,
+ package_json_path,
+ referrer,
));
}
}
Err(throw_exports_not_found(
package_subpath,
- &package_json_url,
- base,
+ package_json_path,
+ referrer,
))
}
fn parse_package_name(
specifier: &str,
- base: &ModuleSpecifier,
+ referrer: &ModuleSpecifier,
) -> Result<(String, String, bool), AnyError> {
let mut separator_index = specifier.find('/');
let mut valid_package_name = true;
@@ -547,7 +572,7 @@ fn parse_package_name(
return Err(errors::err_invalid_module_specifier(
specifier,
"is not a valid package name",
- Some(to_file_path_string(base)),
+ Some(to_specifier_display_string(referrer)),
));
}
@@ -563,27 +588,28 @@ fn parse_package_name(
pub fn package_resolve(
specifier: &str,
referrer: &ModuleSpecifier,
+ referrer_kind: NodeModuleKind,
conditions: &[&str],
npm_resolver: &dyn DenoDirNpmResolver,
-) -> Result<ModuleSpecifier, AnyError> {
+) -> Result<PathBuf, AnyError> {
let (package_name, package_subpath, _is_scoped) =
parse_package_name(specifier, referrer)?;
// ResolveSelf
let package_config = get_package_scope_config(referrer, npm_resolver)?;
- if package_config.exists {
- let package_json_url = Url::from_file_path(&package_config.path).unwrap();
- if package_config.name.as_ref() == Some(&package_name) {
- if let Some(exports) = &package_config.exports {
- return package_exports_resolve(
- package_json_url,
- package_subpath,
- exports,
- referrer,
- conditions,
- npm_resolver,
- );
- }
+ if package_config.exists
+ && package_config.name.as_ref() == Some(&package_name)
+ {
+ if let Some(exports) = &package_config.exports {
+ return package_exports_resolve(
+ &package_config.path,
+ package_subpath,
+ exports,
+ referrer,
+ referrer_kind,
+ conditions,
+ npm_resolver,
+ );
}
}
@@ -592,8 +618,6 @@ pub fn package_resolve(
&referrer.to_file_path().unwrap(),
)?;
let package_json_path = package_dir_path.join("package.json");
- let package_json_url =
- ModuleSpecifier::from_file_path(&package_json_path).unwrap();
// todo: error with this instead when can't find package
// Err(errors::err_module_not_found(
@@ -612,21 +636,20 @@ pub fn package_resolve(
let package_json = PackageJson::load(npm_resolver, package_json_path)?;
if let Some(exports) = &package_json.exports {
return package_exports_resolve(
- package_json_url,
+ &package_json.path,
package_subpath,
exports,
referrer,
+ referrer_kind,
conditions,
npm_resolver,
);
}
if package_subpath == "." {
- return legacy_main_resolve(&package_json_url, &package_json, referrer);
+ return legacy_main_resolve(&package_json, referrer_kind);
}
- package_json_url
- .join(&package_subpath)
- .map_err(AnyError::from)
+ Ok(package_json.path.parent().unwrap().join(&package_subpath))
}
pub fn get_package_scope_config(
@@ -635,12 +658,43 @@ pub fn get_package_scope_config(
) -> Result<PackageJson, AnyError> {
let root_folder = npm_resolver
.resolve_package_folder_from_path(&referrer.to_file_path().unwrap())?;
- let package_json_path = root_folder.join("./package.json");
+ let package_json_path = root_folder.join("package.json");
PackageJson::load(npm_resolver, package_json_path)
}
-fn file_exists(path_url: &ModuleSpecifier) -> bool {
- if let Ok(stats) = std::fs::metadata(to_file_path(path_url)) {
+pub fn get_closest_package_json(
+ url: &ModuleSpecifier,
+ npm_resolver: &dyn DenoDirNpmResolver,
+) -> Result<PackageJson, AnyError> {
+ let package_json_path = get_closest_package_json_path(url, npm_resolver)?;
+ PackageJson::load(npm_resolver, package_json_path)
+}
+
+fn get_closest_package_json_path(
+ url: &ModuleSpecifier,
+ npm_resolver: &dyn DenoDirNpmResolver,
+) -> Result<PathBuf, AnyError> {
+ let file_path = url.to_file_path().unwrap();
+ let mut current_dir = file_path.parent().unwrap();
+ let package_json_path = current_dir.join("package.json");
+ if package_json_path.exists() {
+ return Ok(package_json_path);
+ }
+ let root_pkg_folder = npm_resolver
+ .resolve_package_folder_from_path(&url.to_file_path().unwrap())?;
+ while current_dir.starts_with(&root_pkg_folder) {
+ current_dir = current_dir.parent().unwrap();
+ let package_json_path = current_dir.join("package.json");
+ if package_json_path.exists() {
+ return Ok(package_json_path);
+ }
+ }
+
+ bail!("did not find package.json in {}", root_pkg_folder.display())
+}
+
+fn file_exists(path: &Path) -> bool {
+ if let Ok(stats) = std::fs::metadata(path) {
stats.is_file()
} else {
false
@@ -648,28 +702,56 @@ fn file_exists(path_url: &ModuleSpecifier) -> bool {
}
pub fn legacy_main_resolve(
- package_json_url: &ModuleSpecifier,
package_json: &PackageJson,
- _base: &ModuleSpecifier,
-) -> Result<ModuleSpecifier, AnyError> {
+ referrer_kind: NodeModuleKind,
+) -> Result<PathBuf, AnyError> {
+ let maybe_main =
+ if referrer_kind == NodeModuleKind::Esm && package_json.typ == "module" {
+ &package_json.module
+ } else {
+ &package_json.main
+ };
let mut guess;
- if let Some(main) = &package_json.main {
- guess = package_json_url.join(&format!("./{}", main))?;
+ if let Some(main) = maybe_main {
+ guess = package_json.path.parent().unwrap().join(main).clean();
if file_exists(&guess) {
return Ok(guess);
}
let mut found = false;
- for ext in [
- ".js",
- ".json",
- ".node",
- "/index.js",
- "/index.json",
- "/index.node",
- ] {
- guess = package_json_url.join(&format!("./{}{}", main, ext))?;
+ // todo(dsherret): investigate exactly how node handles this
+ let endings = match referrer_kind {
+ NodeModuleKind::Cjs => vec![
+ ".js",
+ ".cjs",
+ ".json",
+ ".node",
+ "/index.js",
+ "/index.cjs",
+ "/index.json",
+ "/index.node",
+ ],
+ NodeModuleKind::Esm => vec![
+ ".js",
+ ".mjs",
+ ".json",
+ ".node",
+ "/index.js",
+ "/index.mjs",
+ ".cjs",
+ "/index.cjs",
+ "/index.json",
+ "/index.node",
+ ],
+ };
+ for ending in endings {
+ guess = package_json
+ .path
+ .parent()
+ .unwrap()
+ .join(&format!("{}{}", main, ending))
+ .clean();
if file_exists(&guess) {
found = true;
break;
@@ -682,8 +764,25 @@ pub fn legacy_main_resolve(
}
}
- for p in ["./index.js", "./index.json", "./index.node"] {
- guess = package_json_url.join(p)?;
+ let index_file_names = match referrer_kind {
+ NodeModuleKind::Cjs => {
+ vec!["index.js", "index.cjs", "index.json", "index.node"]
+ }
+ NodeModuleKind::Esm => vec![
+ "index.js",
+ "index.mjs",
+ "index.cjs",
+ "index.json",
+ "index.node",
+ ],
+ };
+ for index_file_name in index_file_names {
+ guess = package_json
+ .path
+ .parent()
+ .unwrap()
+ .join(index_file_name)
+ .clean();
if file_exists(&guess) {
// TODO(bartlomieju): emitLegacyIndexDeprecation()
return Ok(guess);