diff options
Diffstat (limited to 'ext')
-rw-r--r-- | ext/node/lib.rs | 2 | ||||
-rw-r--r-- | ext/node/ops/require.rs | 5 | ||||
-rw-r--r-- | ext/node/polyfills/01_require.js | 32 | ||||
-rw-r--r-- | ext/node/resolution.rs | 159 |
4 files changed, 111 insertions, 87 deletions
diff --git a/ext/node/lib.rs b/ext/node/lib.rs index a623eac11..3698e5376 100644 --- a/ext/node/lib.rs +++ b/ext/node/lib.rs @@ -81,7 +81,7 @@ pub trait NpmResolver: std::fmt::Debug + MaybeSend + MaybeSync { fn resolve_package_folder_from_path( &self, path: &Path, - ) -> Result<PathBuf, AnyError>; + ) -> Result<Option<PathBuf>, AnyError>; /// Resolves an npm package folder path from a Deno module. fn resolve_package_folder_from_deno_module( diff --git a/ext/node/ops/require.rs b/ext/node/ops/require.rs index 87b75c88c..f758ec973 100644 --- a/ext/node/ops/require.rs +++ b/ext/node/ops/require.rs @@ -371,7 +371,8 @@ where &Url::from_file_path(parent_path.unwrap()).unwrap(), permissions, ) - .ok(); + .ok() + .flatten(); if pkg.is_none() { return Ok(None); } @@ -499,7 +500,7 @@ where fn op_require_read_closest_package_json<P>( state: &mut OpState, filename: String, -) -> Result<PackageJson, AnyError> +) -> Result<Option<PackageJson>, AnyError> where P: NodePermissions + 'static, { diff --git a/ext/node/polyfills/01_require.js b/ext/node/polyfills/01_require.js index b27565a40..0fe75107d 100644 --- a/ext/node/polyfills/01_require.js +++ b/ext/node/polyfills/01_require.js @@ -879,7 +879,9 @@ Module.prototype.load = function (filename) { StringPrototypeEndsWith(filename, ".mjs") && !Module._extensions[".mjs"] ) { // TODO: use proper error class - throw new Error("require ESM", filename); + throw new Error( + requireEsmErrorText(filename, moduleParentCache.get(this)?.filename), + ); } Module._extensions[extension](this, filename); @@ -990,23 +992,29 @@ Module._extensions[".js"] = function (module, filename) { const content = ops.op_require_read_file(filename); if (StringPrototypeEndsWith(filename, ".js")) { - const pkg = ops.op_require_read_package_scope(filename); - if (pkg && pkg.exists && pkg.typ == "module") { - let message = `Trying to import ESM module: ${filename}`; - - if (module.parent) { - message += ` from ${module.parent.filename}`; - } - - message += ` using require()`; - - throw new Error(message); + const pkg = ops.op_require_read_closest_package_json(filename); + if (pkg && pkg.exists && pkg.typ === "module") { + throw new Error( + requireEsmErrorText(filename, moduleParentCache.get(module)?.filename), + ); } } module._compile(content, filename); }; +function requireEsmErrorText(filename, parent) { + let message = `[ERR_REQUIRE_ESM]: require() of ES Module ${filename}`; + + if (parent) { + message += ` from ${parent}`; + } + + message += + ` not supported. Instead change the require to a dynamic import() which is available in all CommonJS modules.`; + return message; +} + function stripBOM(content) { if (StringPrototypeCharCodeAt(content, 0) === 0xfeff) { content = StringPrototypeSlice(content, 1); diff --git a/ext/node/resolution.rs b/ext/node/resolution.rs index 6db2a9655..8470eba6b 100644 --- a/ext/node/resolution.rs +++ b/ext/node/resolution.rs @@ -426,12 +426,12 @@ impl NodeResolver { if url_str.starts_with("http") { Ok(NodeResolution::Esm(url)) } else if url_str.ends_with(".js") || url_str.ends_with(".d.ts") { - let package_config = + let maybe_package_config = self.get_closest_package_json(&url, &AllowAllNodePermissions)?; - if package_config.typ == "module" { - Ok(NodeResolution::Esm(url)) - } else { - Ok(NodeResolution::CommonJs(url)) + match maybe_package_config { + Some(c) if c.typ == "module" => Ok(NodeResolution::Esm(url)), + Some(_) => Ok(NodeResolution::CommonJs(url)), + None => Ok(NodeResolution::Esm(url)), } } else if url_str.ends_with(".mjs") || url_str.ends_with(".d.mts") { Ok(NodeResolution::Esm(url)) @@ -555,63 +555,22 @@ impl NodeResolver { )); } - let package_config = - self.get_package_scope_config(referrer, permissions)?; let mut package_json_path = None; - if package_config.exists { - 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 = self.resolve_package_target( - package_json_path.as_ref().unwrap(), - imports.get(name).unwrap().to_owned(), - "".to_string(), - name.to_string(), - referrer, - referrer_kind, - false, - true, - conditions, - mode, - permissions, - )?; - if let Some(resolved) = maybe_resolved { - return Ok(resolved); - } - } else { - let mut best_match = ""; - let mut best_match_subpath = None; - for key in imports.keys() { - let pattern_index = key.find('*'); - if let Some(pattern_index) = pattern_index { - let key_sub = &key[0..=pattern_index]; - if name.starts_with(key_sub) { - let pattern_trailer = &key[pattern_index + 1..]; - if name.len() > key.len() - && name.ends_with(&pattern_trailer) - && pattern_key_compare(best_match, key) == 1 - && key.rfind('*') == Some(pattern_index) - { - best_match = key; - best_match_subpath = Some( - name[pattern_index..=(name.len() - pattern_trailer.len())] - .to_string(), - ); - } - } - } - } - - if !best_match.is_empty() { - let target = imports.get(best_match).unwrap().to_owned(); + if let Some(package_config) = + self.get_package_scope_config(referrer, permissions)? + { + if package_config.exists { + 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 = self.resolve_package_target( package_json_path.as_ref().unwrap(), - target, - best_match_subpath.unwrap(), - best_match.to_string(), + imports.get(name).unwrap().to_owned(), + "".to_string(), + name.to_string(), referrer, referrer_kind, - true, + false, true, conditions, mode, @@ -620,6 +579,50 @@ impl NodeResolver { if let Some(resolved) = maybe_resolved { return Ok(resolved); } + } else { + let mut best_match = ""; + let mut best_match_subpath = None; + for key in imports.keys() { + let pattern_index = key.find('*'); + if let Some(pattern_index) = pattern_index { + let key_sub = &key[0..=pattern_index]; + if name.starts_with(key_sub) { + let pattern_trailer = &key[pattern_index + 1..]; + if name.len() > key.len() + && name.ends_with(&pattern_trailer) + && pattern_key_compare(best_match, key) == 1 + && key.rfind('*') == Some(pattern_index) + { + best_match = key; + best_match_subpath = Some( + name + [pattern_index..=(name.len() - pattern_trailer.len())] + .to_string(), + ); + } + } + } + } + + if !best_match.is_empty() { + let target = imports.get(best_match).unwrap().to_owned(); + let maybe_resolved = self.resolve_package_target( + package_json_path.as_ref().unwrap(), + target, + best_match_subpath.unwrap(), + best_match.to_string(), + referrer, + referrer_kind, + true, + true, + conditions, + mode, + permissions, + )?; + if let Some(resolved) = maybe_resolved { + return Ok(resolved); + } + } } } } @@ -988,8 +991,10 @@ impl NodeResolver { parse_package_name(specifier, referrer)?; // ResolveSelf - let package_config = - self.get_package_scope_config(referrer, permissions)?; + let Some(package_config) = + self.get_package_scope_config(referrer, permissions)? else { + return Ok(None); + }; if package_config.exists && package_config.name.as_ref() == Some(&package_name) { @@ -1063,27 +1068,35 @@ impl NodeResolver { &self, referrer: &ModuleSpecifier, permissions: &dyn NodePermissions, - ) -> Result<PackageJson, AnyError> { - let root_folder = self + ) -> Result<Option<PackageJson>, AnyError> { + let Some(root_folder) = self .npm_resolver - .resolve_package_folder_from_path(&referrer.to_file_path().unwrap())?; + .resolve_package_folder_from_path(&referrer.to_file_path().unwrap())? else { + return Ok(None); + }; let package_json_path = root_folder.join("package.json"); - self.load_package_json(permissions, package_json_path) + self + .load_package_json(permissions, package_json_path) + .map(Some) } pub(super) fn get_closest_package_json( &self, url: &ModuleSpecifier, permissions: &dyn NodePermissions, - ) -> Result<PackageJson, AnyError> { - let package_json_path = self.get_closest_package_json_path(url)?; - self.load_package_json(permissions, package_json_path) + ) -> Result<Option<PackageJson>, AnyError> { + let Some(package_json_path) = self.get_closest_package_json_path(url)? else { + return Ok(None); + }; + self + .load_package_json(permissions, package_json_path) + .map(Some) } fn get_closest_package_json_path( &self, url: &ModuleSpecifier, - ) -> Result<PathBuf, AnyError> { + ) -> Result<Option<PathBuf>, AnyError> { let file_path = url.to_file_path().unwrap(); let current_dir = deno_core::strip_unc_prefix( self.fs.realpath_sync(file_path.parent().unwrap())?, @@ -1091,20 +1104,22 @@ impl NodeResolver { let mut current_dir = current_dir.as_path(); let package_json_path = current_dir.join("package.json"); if self.fs.exists(&package_json_path) { - return Ok(package_json_path); + return Ok(Some(package_json_path)); } - let root_pkg_folder = self + let Some(root_pkg_folder) = self .npm_resolver - .resolve_package_folder_from_path(current_dir)?; + .resolve_package_folder_from_path(current_dir)? else { + return Ok(None); + }; while current_dir.starts_with(&root_pkg_folder) { current_dir = current_dir.parent().unwrap(); let package_json_path = current_dir.join("package.json"); if self.fs.exists(&package_json_path) { - return Ok(package_json_path); + return Ok(Some(package_json_path)); } } - bail!("did not find package.json in {}", root_pkg_folder.display()) + Ok(None) } pub(super) fn load_package_json( |