summaryrefslogtreecommitdiff
path: root/cli/node/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'cli/node/mod.rs')
-rw-r--r--cli/node/mod.rs180
1 files changed, 155 insertions, 25 deletions
diff --git a/cli/node/mod.rs b/cli/node/mod.rs
index 97240cfe7..56f228d3f 100644
--- a/cli/node/mod.rs
+++ b/cli/node/mod.rs
@@ -31,6 +31,7 @@ use deno_runtime::deno_node::PathClean;
use deno_runtime::deno_node::RequireNpmResolver;
use deno_runtime::deno_node::DEFAULT_CONDITIONS;
use deno_runtime::deno_node::NODE_GLOBAL_THIS_NAME;
+use deno_runtime::deno_node::TYPES_CONDITIONS;
use once_cell::sync::Lazy;
use regex::Regex;
@@ -55,9 +56,61 @@ impl NodeResolution {
match self {
Self::Esm(u) => u,
Self::CommonJs(u) => u,
- _ => unreachable!(),
+ Self::BuiltIn(specifier) => {
+ if specifier.starts_with("node:") {
+ ModuleSpecifier::parse(&specifier).unwrap()
+ } else {
+ ModuleSpecifier::parse(&format!("node:{}", specifier)).unwrap()
+ }
+ }
}
}
+
+ pub fn into_specifier_and_media_type(
+ resolution: Option<Self>,
+ ) -> (ModuleSpecifier, MediaType) {
+ match resolution {
+ Some(NodeResolution::CommonJs(specifier)) => {
+ let media_type = MediaType::from(&specifier);
+ (
+ specifier,
+ match media_type {
+ MediaType::JavaScript | MediaType::Jsx => MediaType::Cjs,
+ MediaType::TypeScript | MediaType::Tsx => MediaType::Cts,
+ MediaType::Dts => MediaType::Dcts,
+ _ => media_type,
+ },
+ )
+ }
+ Some(NodeResolution::Esm(specifier)) => {
+ let media_type = MediaType::from(&specifier);
+ (
+ specifier,
+ match media_type {
+ MediaType::JavaScript | MediaType::Jsx => MediaType::Mjs,
+ MediaType::TypeScript | MediaType::Tsx => MediaType::Mts,
+ MediaType::Dts => MediaType::Dmts,
+ _ => media_type,
+ },
+ )
+ }
+ maybe_response => {
+ let specifier = match maybe_response {
+ Some(response) => response.into_url(),
+ None => {
+ ModuleSpecifier::parse("deno:///missing_dependency.d.ts").unwrap()
+ }
+ };
+ (specifier, MediaType::Dts)
+ }
+ }
+ }
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum NodeResolutionMode {
+ Execution,
+ Types,
}
struct NodeModulePolyfill {
@@ -389,6 +442,7 @@ pub async fn initialize_binary_command(
pub fn node_resolve(
specifier: &str,
referrer: &ModuleSpecifier,
+ mode: NodeResolutionMode,
npm_resolver: &dyn RequireNpmResolver,
) -> Result<Option<NodeResolution>, AnyError> {
// Note: if we are here, then the referrer is an esm module
@@ -425,12 +479,22 @@ pub fn node_resolve(
}
}
- let conditions = DEFAULT_CONDITIONS;
+ let conditions = mode_conditions(mode);
let url = module_resolve(specifier, referrer, conditions, npm_resolver)?;
let url = match url {
Some(url) => url,
None => return Ok(None),
};
+ let url = match mode {
+ NodeResolutionMode::Execution => url,
+ NodeResolutionMode::Types => {
+ let path = url.to_file_path().unwrap();
+ // todo(16370): the module kind is not correct here. I think we need
+ // typescript to tell us if the referrer is esm or cjs
+ let path = path_to_declaration_path(path, NodeModuleKind::Esm);
+ ModuleSpecifier::from_file_path(path).unwrap()
+ }
+ };
let resolve_response = url_to_node_resolution(url, npm_resolver)?;
// TODO(bartlomieju): skipped checking errors for commonJS resolution and
@@ -440,24 +504,36 @@ pub fn node_resolve(
pub fn node_resolve_npm_reference(
reference: &NpmPackageReference,
+ mode: NodeResolutionMode,
npm_resolver: &NpmPackageResolver,
) -> Result<Option<NodeResolution>, AnyError> {
let package_folder =
npm_resolver.resolve_package_folder_from_deno_module(&reference.req)?;
- let resolved_path = package_config_resolve(
+ let node_module_kind = NodeModuleKind::Esm;
+ let maybe_resolved_path = package_config_resolve(
&reference
.sub_path
.as_ref()
.map(|s| format!("./{}", s))
.unwrap_or_else(|| ".".to_string()),
&package_folder,
+ node_module_kind,
+ mode_conditions(mode),
npm_resolver,
- NodeModuleKind::Esm,
)
.with_context(|| {
format!("Error resolving package config for '{}'.", reference)
})?;
-
+ let resolved_path = match maybe_resolved_path {
+ Some(resolved_path) => resolved_path,
+ None => return Ok(None),
+ };
+ let resolved_path = match mode {
+ NodeResolutionMode::Execution => resolved_path,
+ NodeResolutionMode::Types => {
+ path_to_declaration_path(resolved_path, node_module_kind)
+ }
+ };
let url = ModuleSpecifier::from_file_path(resolved_path).unwrap();
let resolve_response = url_to_node_resolution(url, npm_resolver)?;
// TODO(bartlomieju): skipped checking errors for commonJS resolution and
@@ -465,6 +541,41 @@ pub fn node_resolve_npm_reference(
Ok(Some(resolve_response))
}
+fn mode_conditions(mode: NodeResolutionMode) -> &'static [&'static str] {
+ match mode {
+ NodeResolutionMode::Execution => DEFAULT_CONDITIONS,
+ NodeResolutionMode::Types => TYPES_CONDITIONS,
+ }
+}
+
+/// Checks if the resolved file has a corresponding declaration file.
+fn path_to_declaration_path(
+ path: PathBuf,
+ referrer_kind: NodeModuleKind,
+) -> PathBuf {
+ let lowercase_path = path.to_string_lossy().to_lowercase();
+ if lowercase_path.ends_with(".d.ts")
+ || lowercase_path.ends_with(".d.cts")
+ || lowercase_path.ends_with(".d.ts")
+ {
+ return path;
+ }
+ let specific_dts_path = match referrer_kind {
+ NodeModuleKind::Cjs => path.with_extension("d.cts"),
+ NodeModuleKind::Esm => path.with_extension("d.mts"),
+ };
+ if specific_dts_path.exists() {
+ specific_dts_path
+ } else {
+ let dts_path = path.with_extension("d.ts");
+ if dts_path.exists() {
+ dts_path
+ } else {
+ path
+ }
+ }
+}
+
pub fn node_resolve_binary_export(
pkg_req: &NpmPackageReq,
bin_name: Option<&str>,
@@ -562,45 +673,56 @@ pub fn load_cjs_module_from_ext_node(
fn package_config_resolve(
package_subpath: &str,
package_dir: &Path,
- npm_resolver: &dyn RequireNpmResolver,
referrer_kind: NodeModuleKind,
-) -> Result<PathBuf, AnyError> {
+ conditions: &[&str],
+ npm_resolver: &dyn RequireNpmResolver,
+) -> Result<Option<PathBuf>, AnyError> {
let package_json_path = package_dir.join("package.json");
let referrer = ModuleSpecifier::from_directory_path(package_dir).unwrap();
let package_config =
PackageJson::load(npm_resolver, package_json_path.clone())?;
if let Some(exports) = &package_config.exports {
+ let is_types = conditions == TYPES_CONDITIONS;
+ if is_types && package_subpath == "." {
+ if let Ok(Some(path)) =
+ legacy_main_resolve(&package_config, referrer_kind, conditions)
+ {
+ return Ok(Some(path));
+ }
+ }
return package_exports_resolve(
&package_json_path,
package_subpath.to_string(),
exports,
&referrer,
referrer_kind,
- DEFAULT_CONDITIONS,
+ conditions,
npm_resolver,
- );
+ )
+ .map(Some);
}
if package_subpath == "." {
- return legacy_main_resolve(&package_config, referrer_kind);
+ return legacy_main_resolve(&package_config, referrer_kind, conditions);
}
- Ok(package_dir.join(package_subpath))
+ Ok(Some(package_dir.join(package_subpath)))
}
pub fn url_to_node_resolution(
url: ModuleSpecifier,
npm_resolver: &dyn RequireNpmResolver,
) -> Result<NodeResolution, AnyError> {
- Ok(if url.as_str().starts_with("http") {
+ let url_str = url.as_str().to_lowercase();
+ Ok(if url_str.starts_with("http") {
NodeResolution::Esm(url)
- } else if url.as_str().ends_with(".js") {
+ } else if url_str.ends_with(".js") || url_str.ends_with(".d.ts") {
let package_config = get_closest_package_json(&url, npm_resolver)?;
if package_config.typ == "module" {
NodeResolution::Esm(url)
} else {
NodeResolution::CommonJs(url)
}
- } else if url.as_str().ends_with(".mjs") {
+ } else if url_str.ends_with(".mjs") || url_str.ends_with(".d.mts") {
NodeResolution::Esm(url)
} else {
NodeResolution::CommonJs(url)
@@ -666,7 +788,16 @@ fn module_resolve(
// note: if we're here, the referrer is an esm module
let url = if should_be_treated_as_relative_or_absolute_path(specifier) {
let resolved_specifier = referrer.join(specifier)?;
- Some(resolved_specifier)
+ if conditions == TYPES_CONDITIONS {
+ let file_path = to_file_path(&resolved_specifier);
+ // todo(dsherret): the node module kind is not correct and we
+ // should use the value provided by typescript instead
+ let declaration_path =
+ path_to_declaration_path(file_path, NodeModuleKind::Esm);
+ Some(ModuleSpecifier::from_file_path(declaration_path).unwrap())
+ } else {
+ Some(resolved_specifier)
+ }
} else if specifier.starts_with('#') {
Some(
package_imports_resolve(
@@ -681,16 +812,14 @@ fn module_resolve(
} else if let Ok(resolved) = Url::parse(specifier) {
Some(resolved)
} else {
- Some(
- package_resolve(
- specifier,
- referrer,
- NodeModuleKind::Esm,
- conditions,
- npm_resolver,
- )
- .map(|p| ModuleSpecifier::from_file_path(p).unwrap())?,
- )
+ package_resolve(
+ specifier,
+ referrer,
+ NodeModuleKind::Esm,
+ conditions,
+ npm_resolver,
+ )?
+ .map(|p| ModuleSpecifier::from_file_path(p).unwrap())
};
Ok(match url {
Some(url) => Some(finalize_resolution(url, referrer)?),
@@ -913,6 +1042,7 @@ fn resolve(
let module_dir = npm_resolver.resolve_package_folder_from_package(
package_specifier.as_str(),
&referrer_path,
+ conditions,
)?;
let package_json_path = module_dir.join("package.json");