diff --git a/Cargo.lock b/Cargo.lock index 392ff5ef46..cd16ac1b7e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1532,9 +1532,9 @@ dependencies = [ [[package]] name = "deno_core" -version = "0.330.0" +version = "0.331.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd38bbbd68ed873165ccb630322704b44140d3a8c8d50f898beac4d1a8a3358c" +checksum = "ce2d1779358cad2bc56d71176298767be628d707bb75585f6f8a4be2da8ccda1" dependencies = [ "anyhow", "az", @@ -1658,9 +1658,9 @@ dependencies = [ [[package]] name = "deno_error" -version = "0.5.3" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4da6a58de6932a96f84e133c072fd3b525966ee122a71f3efd48bbff2eed5ac" +checksum = "9c23dbc46d5804814b08b4675838f9884e3a52916987ec5105af36d42f9911b5" dependencies = [ "deno_error_macro", "libc", @@ -1672,9 +1672,9 @@ dependencies = [ [[package]] name = "deno_error_macro" -version = "0.5.3" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46351dff93aed2039407c91e2ded2a5591e42d2795ab3d111288625bb710d3d2" +checksum = "babccedee31ce7e57c3e6dff2cb3ab8d68c49d0df8222fe0d11d628e65192790" dependencies = [ "proc-macro2", "quote", @@ -2151,9 +2151,9 @@ dependencies = [ [[package]] name = "deno_ops" -version = "0.206.0" +version = "0.207.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c25ffa9d088ea00748dbef870bba110ac22ebf8cf7b2e9eb288409c5d852af3" +checksum = "96f000a21f6969b4c945bc8e9e785aa439f11ca4fd3fbddcd5bebc102167eb37" dependencies = [ "indexmap 2.3.0", "proc-macro-rules", @@ -6947,9 +6947,9 @@ dependencies = [ [[package]] name = "serde_v8" -version = "0.239.0" +version = "0.240.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3caa6d882827148e5d9052d9d8d6d1c9d6ad426ed00cab46cafb8c07a0e7126a" +checksum = "cd0494d74c40ab94f53a19485de359ea6a55f05341b817b93440b673c1ce8ec6" dependencies = [ "deno_error", "num-bigint", diff --git a/Cargo.toml b/Cargo.toml index 42c2970e05..83d4a8398a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -49,7 +49,7 @@ repository = "https://github.com/denoland/deno" [workspace.dependencies] deno_ast = { version = "=0.44.0", features = ["transpiling"] } -deno_core = { version = "0.330.0" } +deno_core = { version = "0.331.0" } deno_bench_util = { version = "0.180.0", path = "./bench_util" } deno_config = { version = "=0.45.0", features = ["workspace", "sync"] } @@ -123,7 +123,7 @@ dashmap = "5.5.3" data-encoding = "2.3.3" data-url = "=0.3.1" deno_cache_dir = "=0.16.0" -deno_error = "=0.5.3" +deno_error = "=0.5.5" deno_package_json = { version = "0.4.0", default-features = false } deno_unsync = "0.4.2" dlopen2 = "0.6.1" diff --git a/cli/args/lockfile.rs b/cli/args/lockfile.rs index 976992aac8..5fa6a49c43 100644 --- a/cli/args/lockfile.rs +++ b/cli/args/lockfile.rs @@ -61,11 +61,13 @@ impl<'a, T> std::ops::DerefMut for Guard<'a, T> { } #[derive(Debug, thiserror::Error, deno_error::JsError)] -#[error("Failed writing lockfile")] -#[class(inherit)] -struct AtomicWriteFileWithRetriesError { - #[source] - source: std::io::Error, +pub enum AtomicWriteFileWithRetriesError { + #[class(inherit)] + #[error(transparent)] + Changed(JsErrorBox), + #[class(inherit)] + #[error("Failed writing lockfile")] + Io(#[source] std::io::Error), } impl CliLockfile { @@ -87,12 +89,16 @@ impl CliLockfile { self.lockfile.lock().overwrite } - pub fn write_if_changed(&self) -> Result<(), JsErrorBox> { + pub fn write_if_changed( + &self, + ) -> Result<(), AtomicWriteFileWithRetriesError> { if self.skip_write { return Ok(()); } - self.error_if_changed()?; + self + .error_if_changed() + .map_err(AtomicWriteFileWithRetriesError::Changed)?; let mut lockfile = self.lockfile.lock(); let Some(bytes) = lockfile.resolve_write_bytes() else { return Ok(()); // nothing to do @@ -105,9 +111,7 @@ impl CliLockfile { &bytes, cache::CACHE_PERM, ) - .map_err(|source| { - JsErrorBox::from_err(AtomicWriteFileWithRetriesError { source }) - })?; + .map_err(AtomicWriteFileWithRetriesError::Io)?; lockfile.has_content_changed = false; Ok(()) } diff --git a/cli/args/mod.rs b/cli/args/mod.rs index f77eedc594..46ada5440f 100644 --- a/cli/args/mod.rs +++ b/cli/args/mod.rs @@ -82,6 +82,7 @@ use deno_terminal::colors; use dotenvy::from_filename; pub use flags::*; use import_map::resolve_import_map_value_from_specifier; +pub use lockfile::AtomicWriteFileWithRetriesError; pub use lockfile::CliLockfile; pub use lockfile::CliLockfileReadFromPathOptions; use once_cell::sync::Lazy; diff --git a/cli/emit.rs b/cli/emit.rs index 69ac8323bb..f928591eaf 100644 --- a/cli/emit.rs +++ b/cli/emit.rs @@ -112,9 +112,9 @@ impl Emitter { &self, specifier: &ModuleSpecifier, media_type: MediaType, - module_kind: deno_ast::ModuleKind, + module_kind: ModuleKind, source: &Arc, - ) -> Result { + ) -> Result { // Note: keep this in sync with the sync version below let helper = EmitParsedSourceHelper(self); match helper.pre_emit_parsed_source(specifier, module_kind, source) { diff --git a/cli/main.rs b/cli/main.rs index f97ea81e5d..a6b552c636 100644 --- a/cli/main.rs +++ b/cli/main.rs @@ -201,7 +201,7 @@ async fn run_subcommand(flags: Arc) -> Result { match result { Ok(v) => Ok(v), Err(script_err) => { - if let Some(ResolvePkgFolderFromDenoReqError::Byonm(ByonmResolvePkgFolderFromDenoReqError::UnmatchedReq(_))) = util::result::any_and_jserrorbox_downcast_ref::(&script_err) { + if let Some(worker::CreateCustomWorkerError::ResolvePkgFolderFromDenoReq(ResolvePkgFolderFromDenoReqError::Byonm(ByonmResolvePkgFolderFromDenoReqError::UnmatchedReq(_)))) = util::result::any_and_jserrorbox_downcast_ref::(&script_err) { if flags.node_modules_dir.is_none() { let mut flags = flags.deref().clone(); let watch = match &flags.subcommand { diff --git a/cli/module_loader.rs b/cli/module_loader.rs index 2b0ebca986..7acb947491 100644 --- a/cli/module_loader.rs +++ b/cli/module_loader.rs @@ -13,8 +13,6 @@ use std::sync::Arc; use deno_ast::MediaType; use deno_ast::ModuleKind; -use deno_core::anyhow::anyhow; -use deno_core::anyhow::Context; use deno_core::error::AnyError; use deno_core::error::ModuleLoaderError; use deno_core::futures::future::FutureExt; @@ -45,6 +43,7 @@ use deno_lib::worker::ModuleLoaderFactory; use deno_resolver::npm::DenoInNpmPackageChecker; use deno_runtime::code_cache; use deno_runtime::deno_node::create_host_defined_options; +use deno_runtime::deno_node::ops::require::UnableToGetCwdError; use deno_runtime::deno_node::NodeRequireLoader; use deno_runtime::deno_permissions::PermissionsContainer; use deno_semver::npm::NpmPackageReqReference; @@ -99,6 +98,11 @@ pub enum PrepareModuleLoadError { Check(#[from] CheckError), #[class(inherit)] #[error(transparent)] + AtomicWriteFileWithRetries( + #[from] crate::args::AtomicWriteFileWithRetriesError, + ), + #[class(inherit)] + #[error(transparent)] Other(#[from] JsErrorBox), } @@ -419,6 +423,55 @@ impl ModuleLoaderFactory for CliModuleLoaderFactory { } } +#[derive(Debug, thiserror::Error, deno_error::JsError)] +pub enum LoadCodeSourceError { + #[class(inherit)] + #[error(transparent)] + NpmModuleLoad(crate::resolver::NpmModuleLoadError), + #[class(inherit)] + #[error(transparent)] + LoadPreparedModule(#[from] LoadPreparedModuleError), + #[class(generic)] + #[error("Loading unprepared module: {}{}", .specifier, .maybe_referrer.as_ref().map(|r| format!(", imported from: {}", r)).unwrap_or_default())] + LoadUnpreparedModule { + specifier: ModuleSpecifier, + maybe_referrer: Option, + }, +} + +#[derive(Debug, thiserror::Error, deno_error::JsError)] +pub enum LoadPreparedModuleError { + #[class(inherit)] + #[error(transparent)] + NpmModuleLoad(#[from] crate::emit::EmitParsedSourceHelperError), + #[class(inherit)] + #[error(transparent)] + LoadMaybeCjs(#[from] LoadMaybeCjsError), + #[class(inherit)] + #[error(transparent)] + Other(#[from] JsErrorBox), +} + +#[derive(Debug, thiserror::Error, deno_error::JsError)] +pub enum LoadMaybeCjsError { + #[class(inherit)] + #[error(transparent)] + NpmModuleLoad(#[from] crate::emit::EmitParsedSourceHelperError), + #[class(inherit)] + #[error(transparent)] + TranslateCjsToEsm(#[from] node_resolver::analyze::TranslateCjsToEsmError), +} + +#[derive(Debug, thiserror::Error, deno_error::JsError)] +#[class(inherit)] +#[error("Could not resolve '{reference}'")] +pub struct CouldNotResolveError { + reference: deno_semver::npm::NpmPackageNvReference, + #[source] + #[inherit] + source: node_resolver::errors::PackageSubpathResolveError, +} + struct CliModuleLoaderInner { lib: TsTypeLib, is_worker: bool, @@ -443,7 +496,10 @@ impl maybe_referrer: Option<&ModuleSpecifier>, requested_module_type: RequestedModuleType, ) -> Result { - let code_source = self.load_code_source(specifier, maybe_referrer).await?; + let code_source = self + .load_code_source(specifier, maybe_referrer) + .await + .map_err(JsErrorBox::from_err)?; let code = if self.shared.is_inspecting || code_source.media_type == MediaType::Wasm { @@ -504,7 +560,7 @@ impl &self, specifier: &ModuleSpecifier, maybe_referrer: Option<&ModuleSpecifier>, - ) -> Result { + ) -> Result { if let Some(code_source) = self.load_prepared_module(specifier).await? { return Ok(code_source); } @@ -513,14 +569,14 @@ impl .shared .npm_module_loader .load(specifier, maybe_referrer) - .await; + .await + .map_err(LoadCodeSourceError::NpmModuleLoad); } - let mut msg = format!("Loading unprepared module: {specifier}"); - if let Some(referrer) = maybe_referrer { - msg = format!("{}, imported from: {}", msg, referrer.as_str()); - } - Err(anyhow!(msg)) + Err(LoadCodeSourceError::LoadUnpreparedModule { + specifier: specifier.clone(), + maybe_referrer: maybe_referrer.cloned(), + }) } fn resolve_referrer( @@ -543,7 +599,8 @@ impl .map_err(|e| e.into()) } else { // this cwd check is slow, so try to avoid it - let cwd = std::env::current_dir().context("Unable to get CWD")?; + let cwd = std::env::current_dir() + .map_err(|e| JsErrorBox::from_err(UnableToGetCwdError(e)))?; deno_core::resolve_path(referrer, &cwd).map_err(|e| e.into()) } } @@ -622,8 +679,11 @@ impl ResolutionMode::Import, NodeResolutionKind::Execution, ) - .with_context(|| { - format!("Could not resolve '{}'.", module.nv_reference) + .map_err(|source| { + JsErrorBox::from_err(CouldNotResolveError { + reference: module.nv_reference.clone(), + source, + }) })? } Some(Module::Node(module)) => module.specifier.clone(), @@ -644,7 +704,7 @@ impl async fn load_prepared_module( &self, specifier: &ModuleSpecifier, - ) -> Result, AnyError> { + ) -> Result, LoadPreparedModuleError> { // Note: keep this in sync with the sync version below let graph = self.graph_container.graph(); match self.load_prepared_module_or_defer_emit(&graph, specifier)? { @@ -676,7 +736,8 @@ impl }) => self .load_maybe_cjs(specifier, media_type, source) .await - .map(Some), + .map(Some) + .map_err(LoadPreparedModuleError::LoadMaybeCjs), None => Ok(None), } } @@ -837,7 +898,7 @@ impl specifier: &ModuleSpecifier, media_type: MediaType, original_source: &Arc, - ) -> Result { + ) -> Result { let js_source = if media_type.is_emittable() { Cow::Owned( self diff --git a/cli/node.rs b/cli/node.rs index 892e25914a..f6411f5e53 100644 --- a/cli/node.rs +++ b/cli/node.rs @@ -5,13 +5,14 @@ use std::sync::Arc; use deno_ast::MediaType; use deno_ast::ModuleSpecifier; -use deno_core::error::AnyError; +use deno_error::JsErrorBox; use deno_graph::ParsedSourceStore; use deno_resolver::npm::DenoInNpmPackageChecker; use deno_runtime::deno_fs; use deno_runtime::deno_node::RealIsBuiltInNodeModuleChecker; use node_resolver::analyze::CjsAnalysis as ExtNodeCjsAnalysis; use node_resolver::analyze::CjsAnalysisExports; +use node_resolver::analyze::CjsCodeAnalysisError; use node_resolver::analyze::CjsCodeAnalyzer; use node_resolver::analyze::NodeCodeTranslator; use serde::Deserialize; @@ -75,7 +76,7 @@ impl CliCjsCodeAnalyzer { &self, specifier: &ModuleSpecifier, source: &str, - ) -> Result { + ) -> Result { let source_hash = CacheDBHash::from_hashable(source); if let Some(analysis) = self.cache.get_cjs_analysis(specifier.as_str(), source_hash) @@ -102,9 +103,10 @@ impl CliCjsCodeAnalyzer { deno_core::unsync::spawn_blocking({ let specifier = specifier.clone(); let source: Arc = source.into(); - move || -> Result<_, AnyError> { - let parsed_source = - maybe_parsed_source.map(Ok).unwrap_or_else(|| { + move || -> Result<_, CjsCodeAnalysisError> { + let parsed_source = maybe_parsed_source + .map(Ok) + .unwrap_or_else(|| { deno_ast::parse_program(deno_ast::ParseParams { specifier, text: source, @@ -113,7 +115,8 @@ impl CliCjsCodeAnalyzer { scope_analysis: false, maybe_syntax: None, }) - })?; + }) + .map_err(JsErrorBox::from_err)?; let is_script = parsed_source.compute_is_script(); let is_cjs = cjs_tracker.is_cjs_with_known_is_script( parsed_source.specifier(), @@ -151,7 +154,7 @@ impl CjsCodeAnalyzer for CliCjsCodeAnalyzer { &self, specifier: &ModuleSpecifier, source: Option>, - ) -> Result, AnyError> { + ) -> Result, CjsCodeAnalysisError> { let source = match source { Some(source) => source, None => { diff --git a/cli/resolver.rs b/cli/resolver.rs index 5677767fdd..b837ba51b1 100644 --- a/cli/resolver.rs +++ b/cli/resolver.rs @@ -1,6 +1,7 @@ // Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; +use std::path::PathBuf; use std::sync::Arc; use async_trait::async_trait; @@ -8,8 +9,6 @@ use dashmap::DashSet; use deno_ast::MediaType; use deno_config::workspace::MappedResolutionDiagnostic; use deno_config::workspace::MappedResolutionError; -use deno_core::anyhow::Context; -use deno_core::error::AnyError; use deno_core::url::Url; use deno_core::ModuleSourceCode; use deno_core::ModuleSpecifier; @@ -84,6 +83,62 @@ pub struct NpmModuleLoader { node_code_translator: Arc, } +#[derive(Debug, Error, deno_error::JsError)] +pub enum NpmModuleLoadError { + #[class(inherit)] + #[error(transparent)] + NotSupportedKindInNpm(#[from] NotSupportedKindInNpmError), + #[class(inherit)] + #[error(transparent)] + ClosestPkgJson(#[from] node_resolver::errors::ClosestPkgJsonError), + #[class(inherit)] + #[error(transparent)] + TranslateCjsToEsm(#[from] node_resolver::analyze::TranslateCjsToEsmError), + #[class(inherit)] + #[error("{}", format_message(file_path, maybe_referrer))] + Fs { + file_path: PathBuf, + maybe_referrer: Option, + #[source] + #[inherit] + source: deno_runtime::deno_io::fs::FsError, + }, +} + +fn format_message( + file_path: &std::path::Path, + maybe_referrer: &Option, +) -> String { + if file_path.is_dir() { + // directory imports are not allowed when importing from an + // ES module, so provide the user with a helpful error message + let dir_path = file_path; + let mut msg = "Directory import ".to_string(); + msg.push_str(&dir_path.to_string_lossy()); + if let Some(referrer) = maybe_referrer { + msg.push_str(" is not supported resolving import from "); + msg.push_str(referrer.as_str()); + let entrypoint_name = ["index.mjs", "index.js", "index.cjs"] + .iter() + .find(|e| dir_path.join(e).is_file()); + if let Some(entrypoint_name) = entrypoint_name { + msg.push_str("\nDid you mean to import "); + msg.push_str(entrypoint_name); + msg.push_str(" within the directory?"); + } + } + msg + } else { + let mut msg = "Unable to load ".to_string(); + msg.push_str(&file_path.to_string_lossy()); + if let Some(referrer) = maybe_referrer { + msg.push_str(" imported from "); + msg.push_str(referrer.as_str()); + } + msg + } +} + impl NpmModuleLoader { pub fn new( cjs_tracker: Arc, @@ -101,50 +156,26 @@ impl NpmModuleLoader { &self, specifier: &ModuleSpecifier, maybe_referrer: Option<&ModuleSpecifier>, - ) -> Result { + ) -> Result { let file_path = specifier.to_file_path().unwrap(); let code = self .fs .read_file_async(file_path.clone(), None) .await - .map_err(AnyError::from) - .with_context(|| { - if file_path.is_dir() { - // directory imports are not allowed when importing from an - // ES module, so provide the user with a helpful error message - let dir_path = file_path; - let mut msg = "Directory import ".to_string(); - msg.push_str(&dir_path.to_string_lossy()); - if let Some(referrer) = &maybe_referrer { - msg.push_str(" is not supported resolving import from "); - msg.push_str(referrer.as_str()); - let entrypoint_name = ["index.mjs", "index.js", "index.cjs"] - .iter() - .find(|e| dir_path.join(e).is_file()); - if let Some(entrypoint_name) = entrypoint_name { - msg.push_str("\nDid you mean to import "); - msg.push_str(entrypoint_name); - msg.push_str(" within the directory?"); - } - } - msg - } else { - let mut msg = "Unable to load ".to_string(); - msg.push_str(&file_path.to_string_lossy()); - if let Some(referrer) = &maybe_referrer { - msg.push_str(" imported from "); - msg.push_str(referrer.as_str()); - } - msg - } + .map_err(|source| NpmModuleLoadError::Fs { + file_path, + maybe_referrer: maybe_referrer.cloned(), + source, })?; let media_type = MediaType::from_specifier(specifier); if media_type.is_emittable() { - return Err(AnyError::from(NotSupportedKindInNpmError { - media_type, - specifier: specifier.clone(), - })); + return Err(NpmModuleLoadError::NotSupportedKindInNpm( + NotSupportedKindInNpmError { + media_type, + specifier: specifier.clone(), + }, + )); } let code = if self.cjs_tracker.is_maybe_cjs(specifier, media_type)? { diff --git a/cli/standalone/binary.rs b/cli/standalone/binary.rs index 5334b4719d..54c82d17a3 100644 --- a/cli/standalone/binary.rs +++ b/cli/standalone/binary.rs @@ -37,6 +37,7 @@ use deno_core::futures::AsyncReadExt; use deno_core::futures::AsyncSeekExt; use deno_core::serde_json; use deno_core::url::Url; +use deno_error::JsErrorBox; use deno_graph::ModuleGraph; use deno_lib::cache::DenoDir; use deno_lib::standalone::virtual_fs::FileSystemCaseSensitivity; @@ -278,7 +279,7 @@ impl StandaloneModules { pub fn resolve_specifier<'a>( &'a self, specifier: &'a ModuleSpecifier, - ) -> Result, AnyError> { + ) -> Result, JsErrorBox> { if specifier.scheme() == "file" { Ok(Some(specifier)) } else { diff --git a/cli/standalone/mod.rs b/cli/standalone/mod.rs index f2a0859e8f..1ab77a5a4c 100644 --- a/cli/standalone/mod.rs +++ b/cli/standalone/mod.rs @@ -414,7 +414,8 @@ impl ModuleLoader for EmbeddedModuleLoader { let code_source = shared .npm_module_loader .load(&original_specifier, maybe_referrer.as_ref()) - .await?; + .await + .map_err(JsErrorBox::from_err)?; let code_cache_entry = shared.get_code_cache( &code_source.found_url, code_source.code.as_bytes(), @@ -477,7 +478,8 @@ impl ModuleLoader for EmbeddedModuleLoader { let source = shared .node_code_translator .translate_cjs_to_esm(&module_specifier, Some(source)) - .await?; + .await + .map_err(JsErrorBox::from_err)?; let module_source = match source { Cow::Owned(source) => ModuleSourceCode::String(source.into()), Cow::Borrowed(source) => { diff --git a/cli/standalone/serialization.rs b/cli/standalone/serialization.rs index ab345917a3..00a0a04997 100644 --- a/cli/standalone/serialization.rs +++ b/cli/standalone/serialization.rs @@ -17,6 +17,7 @@ use deno_core::url::Url; use deno_core::FastString; use deno_core::ModuleSourceCode; use deno_core::ModuleType; +use deno_error::JsErrorBox; use deno_lib::standalone::virtual_fs::VirtualDirectoryEntries; use deno_npm::resolution::SerializedNpmResolutionSnapshot; use deno_npm::resolution::SerializedNpmResolutionSnapshotPackage; @@ -441,12 +442,15 @@ impl RemoteModulesStore { pub fn resolve_specifier<'a>( &'a self, specifier: &'a Url, - ) -> Result, AnyError> { + ) -> Result, JsErrorBox> { let mut count = 0; let mut current = specifier; loop { if count > 10 { - bail!("Too many redirects resolving '{}'", specifier); + return Err(JsErrorBox::generic(format!( + "Too many redirects resolving '{}'", + specifier + ))); } match self.specifiers.get(current) { Some(RemoteModulesStoreSpecifierValue::Redirect(to)) => { diff --git a/cli/tools/bench/mod.rs b/cli/tools/bench/mod.rs index 6a57c4ce6c..a316e60b52 100644 --- a/cli/tools/bench/mod.rs +++ b/cli/tools/bench/mod.rs @@ -48,6 +48,7 @@ use crate::util::fs::collect_specifiers; use crate::util::path::is_script_ext; use crate::util::path::matches_pattern_or_exact_path; use crate::worker::CliMainWorkerFactory; +use crate::worker::CreateCustomWorkerError; mod mitata; mod reporters; @@ -164,7 +165,7 @@ async fn bench_specifier( .await { Ok(()) => Ok(()), - Err(CoreError::Js(error)) => { + Err(CreateCustomWorkerError::Core(CoreError::Js(error))) => { sender.send(BenchEvent::UncaughtError( specifier.to_string(), Box::new(error), @@ -182,7 +183,7 @@ async fn bench_specifier_inner( specifier: ModuleSpecifier, sender: &UnboundedSender, filter: TestFilter, -) -> Result<(), CoreError> { +) -> Result<(), CreateCustomWorkerError> { let mut worker = worker_factory .create_custom_worker( WorkerExecutionMode::Bench, @@ -201,7 +202,7 @@ async fn bench_specifier_inner( // Ensure that there are no pending exceptions before we start running tests worker.run_up_to_duration(Duration::from_millis(0)).await?; - worker.dispatch_load_event()?; + worker.dispatch_load_event().map_err(CoreError::Js)?; let benchmarks = { let state_rc = worker.js_runtime.op_state(); @@ -236,11 +237,13 @@ async fn bench_specifier_inner( used_only, names: benchmarks.iter().map(|(d, _)| d.name.clone()).collect(), })) - .map_err(JsErrorBox::from_err)?; + .map_err(JsErrorBox::from_err) + .map_err(CoreError::JsBox)?; for (desc, function) in benchmarks { sender .send(BenchEvent::Wait(desc.id)) - .map_err(JsErrorBox::from_err)?; + .map_err(JsErrorBox::from_err) + .map_err(CoreError::JsBox)?; let call = worker.js_runtime.call(&function); let result = worker .js_runtime @@ -249,18 +252,26 @@ async fn bench_specifier_inner( let scope = &mut worker.js_runtime.handle_scope(); let result = v8::Local::new(scope, result); let result = serde_v8::from_v8::(scope, result) - .map_err(JsErrorBox::from_err)?; + .map_err(JsErrorBox::from_err) + .map_err(CoreError::JsBox)?; sender .send(BenchEvent::Result(desc.id, result)) - .map_err(JsErrorBox::from_err)?; + .map_err(JsErrorBox::from_err) + .map_err(CoreError::JsBox)?; } // Ignore `defaultPrevented` of the `beforeunload` event. We don't allow the // event loop to continue beyond what's needed to await results. - worker.dispatch_beforeunload_event()?; - worker.dispatch_process_beforeexit_event()?; - worker.dispatch_unload_event()?; - worker.dispatch_process_exit_event()?; + worker + .dispatch_beforeunload_event() + .map_err(CoreError::Js)?; + worker + .dispatch_process_beforeexit_event() + .map_err(CoreError::Js)?; + worker.dispatch_unload_event().map_err(CoreError::Js)?; + worker + .dispatch_process_exit_event() + .map_err(CoreError::Js)?; // Ensure the worker has settled so we can catch any remaining unhandled rejections. We don't // want to wait forever here. diff --git a/cli/tools/coverage/mod.rs b/cli/tools/coverage/mod.rs index 9b6ef81ea3..53c08191f7 100644 --- a/cli/tools/coverage/mod.rs +++ b/cli/tools/coverage/mod.rs @@ -18,10 +18,12 @@ use deno_config::glob::PathOrPatternSet; use deno_core::anyhow::anyhow; use deno_core::anyhow::Context; use deno_core::error::AnyError; +use deno_core::error::CoreError; use deno_core::serde_json; use deno_core::sourcemap::SourceMap; use deno_core::url::Url; use deno_core::LocalInspectorSession; +use deno_error::JsErrorBox; use deno_resolver::npm::DenoInNpmPackageChecker; use node_resolver::InNpmPackageChecker; use regex::Regex; @@ -53,7 +55,7 @@ pub struct CoverageCollector { #[async_trait::async_trait(?Send)] impl crate::worker::CoverageCollector for CoverageCollector { - async fn start_collecting(&mut self) -> Result<(), AnyError> { + async fn start_collecting(&mut self) -> Result<(), CoreError> { self.enable_debugger().await?; self.enable_profiler().await?; self @@ -67,7 +69,7 @@ impl crate::worker::CoverageCollector for CoverageCollector { Ok(()) } - async fn stop_collecting(&mut self) -> Result<(), AnyError> { + async fn stop_collecting(&mut self) -> Result<(), CoreError> { fs::create_dir_all(&self.dir)?; let script_coverages = self.take_precise_coverage().await?.result; @@ -88,7 +90,8 @@ impl crate::worker::CoverageCollector for CoverageCollector { let filepath = self.dir.join(filename); let mut out = BufWriter::new(File::create(&filepath)?); - let coverage = serde_json::to_string(&script_coverage)?; + let coverage = serde_json::to_string(&script_coverage) + .map_err(JsErrorBox::from_err)?; let formatted_coverage = format_json(&filepath, &coverage, &Default::default()) .ok() @@ -111,7 +114,7 @@ impl CoverageCollector { Self { dir, session } } - async fn enable_debugger(&mut self) -> Result<(), AnyError> { + async fn enable_debugger(&mut self) -> Result<(), CoreError> { self .session .post_message::<()>("Debugger.enable", None) @@ -119,7 +122,7 @@ impl CoverageCollector { Ok(()) } - async fn enable_profiler(&mut self) -> Result<(), AnyError> { + async fn enable_profiler(&mut self) -> Result<(), CoreError> { self .session .post_message::<()>("Profiler.enable", None) @@ -127,7 +130,7 @@ impl CoverageCollector { Ok(()) } - async fn disable_debugger(&mut self) -> Result<(), AnyError> { + async fn disable_debugger(&mut self) -> Result<(), CoreError> { self .session .post_message::<()>("Debugger.disable", None) @@ -135,7 +138,7 @@ impl CoverageCollector { Ok(()) } - async fn disable_profiler(&mut self) -> Result<(), AnyError> { + async fn disable_profiler(&mut self) -> Result<(), CoreError> { self .session .post_message::<()>("Profiler.disable", None) @@ -146,26 +149,28 @@ impl CoverageCollector { async fn start_precise_coverage( &mut self, parameters: cdp::StartPreciseCoverageArgs, - ) -> Result { + ) -> Result { let return_value = self .session .post_message("Profiler.startPreciseCoverage", Some(parameters)) .await?; - let return_object = serde_json::from_value(return_value)?; + let return_object = + serde_json::from_value(return_value).map_err(JsErrorBox::from_err)?; Ok(return_object) } async fn take_precise_coverage( &mut self, - ) -> Result { + ) -> Result { let return_value = self .session .post_message::<()>("Profiler.takePreciseCoverage", None) .await?; - let return_object = serde_json::from_value(return_value)?; + let return_object = + serde_json::from_value(return_value).map_err(JsErrorBox::from_err)?; Ok(return_object) } diff --git a/cli/tools/test/mod.rs b/cli/tools/test/mod.rs index 21ee7e152d..cb49a9de95 100644 --- a/cli/tools/test/mod.rs +++ b/cli/tools/test/mod.rs @@ -87,6 +87,7 @@ use crate::util::path::is_script_ext; use crate::util::path::matches_pattern_or_exact_path; use crate::worker::CliMainWorkerFactory; use crate::worker::CoverageCollector; +use crate::worker::CreateCustomWorkerError; mod channel; pub mod fmt; @@ -614,7 +615,10 @@ async fn configure_main_worker( permissions_container: PermissionsContainer, worker_sender: TestEventWorkerSender, options: &TestSpecifierOptions, -) -> Result<(Option>, MainWorker), CoreError> { +) -> Result< + (Option>, MainWorker), + CreateCustomWorkerError, +> { let mut worker = worker_factory .create_custom_worker( WorkerExecutionMode::Test, @@ -647,7 +651,7 @@ async fn configure_main_worker( &worker.js_runtime.op_state(), TestEvent::UncaughtError(specifier.to_string(), Box::new(err)), ) - .map_err(JsErrorBox::from_err)?; + .map_err(|e| CoreError::JsBox(JsErrorBox::from_err(e)))?; Ok(()) } Err(err) => Err(err), @@ -687,7 +691,7 @@ pub async fn test_specifier( .await { Ok(()) => Ok(()), - Err(CoreError::Js(err)) => { + Err(TestSpecifierError::Core(CoreError::Js(err))) => { send_test_event( &worker.js_runtime.op_state(), TestEvent::UncaughtError(specifier.to_string(), Box::new(err)), @@ -698,6 +702,16 @@ pub async fn test_specifier( } } +#[derive(Debug, thiserror::Error, deno_error::JsError)] +pub enum TestSpecifierError { + #[class(inherit)] + #[error(transparent)] + Core(#[from] CoreError), + #[class(inherit)] + #[error(transparent)] + RunTestsForWorker(#[from] RunTestsForWorkerErr), +} + /// Test a single specifier as documentation containing test programs, an executable test module or /// both. #[allow(clippy::too_many_arguments)] @@ -707,19 +721,21 @@ async fn test_specifier_inner( specifier: ModuleSpecifier, fail_fast_tracker: FailFastTracker, options: TestSpecifierOptions, -) -> Result<(), CoreError> { +) -> Result<(), TestSpecifierError> { // Ensure that there are no pending exceptions before we start running tests worker.run_up_to_duration(Duration::from_millis(0)).await?; - worker.dispatch_load_event()?; + worker.dispatch_load_event().map_err(CoreError::Js)?; run_tests_for_worker(worker, &specifier, &options, &fail_fast_tracker) .await?; // Ignore `defaultPrevented` of the `beforeunload` event. We don't allow the // event loop to continue beyond what's needed to await results. - worker.dispatch_beforeunload_event()?; - worker.dispatch_unload_event()?; + worker + .dispatch_beforeunload_event() + .map_err(CoreError::Js)?; + worker.dispatch_unload_event().map_err(CoreError::Js)?; // Ensure all output has been flushed _ = worker @@ -780,12 +796,25 @@ pub fn send_test_event( .send(event) } +#[derive(Debug, thiserror::Error, deno_error::JsError)] +pub enum RunTestsForWorkerErr { + #[class(inherit)] + #[error(transparent)] + ChannelClosed(#[from] ChannelClosedError), + #[class(inherit)] + #[error(transparent)] + Core(#[from] CoreError), + #[class(inherit)] + #[error(transparent)] + SerdeV8(#[from] serde_v8::Error), +} + pub async fn run_tests_for_worker( worker: &mut MainWorker, specifier: &ModuleSpecifier, options: &TestSpecifierOptions, fail_fast_tracker: &FailFastTracker, -) -> Result<(), AnyError> { +) -> Result<(), RunTestsForWorkerErr> { let state_rc = worker.js_runtime.op_state(); // Take whatever tests have been registered let TestContainer(tests, test_functions) = @@ -814,7 +843,7 @@ async fn run_tests_for_worker_inner( test_functions: Vec>, options: &TestSpecifierOptions, fail_fast_tracker: &FailFastTracker, -) -> Result<(), AnyError> { +) -> Result<(), RunTestsForWorkerErr> { let unfiltered = tests.len(); let state_rc = worker.js_runtime.op_state(); @@ -1109,7 +1138,7 @@ async fn wait_for_activity_to_stabilize( before: RuntimeActivityStats, sanitize_ops: bool, sanitize_resources: bool, -) -> Result, AnyError> { +) -> Result, CoreError> { // First, check to see if there's any diff at all. If not, just continue. let after = stats.clone().capture(filter); let mut diff = RuntimeActivityStats::diff(&before, &after); diff --git a/cli/util/result.rs b/cli/util/result.rs index 0c1a75b1ce..e6d45be470 100644 --- a/cli/util/result.rs +++ b/cli/util/result.rs @@ -36,7 +36,7 @@ pub fn any_and_jserrorbox_downcast_ref< }) .or_else(|| { err.downcast_ref::().and_then(|e| match e { - CoreError::JsNative(e) => e.as_any().downcast_ref::(), + CoreError::JsBox(e) => e.as_any().downcast_ref::(), _ => None, }) }) diff --git a/cli/worker.rs b/cli/worker.rs index cf301de83e..3a11f56c71 100644 --- a/cli/worker.rs +++ b/cli/worker.rs @@ -4,8 +4,6 @@ use std::path::Path; use std::sync::Arc; use deno_ast::ModuleSpecifier; -use deno_core::anyhow::bail; -use deno_core::error::AnyError; use deno_core::error::CoreError; use deno_core::futures::FutureExt; use deno_core::v8; @@ -19,6 +17,7 @@ use deno_runtime::deno_permissions::PermissionsContainer; use deno_runtime::worker::MainWorker; use deno_runtime::WorkerExecutionMode; use deno_semver::npm::NpmPackageReqReference; +use node_resolver::errors::ResolvePkgJsonBinExportError; use node_resolver::NodeResolutionKind; use node_resolver::ResolutionMode; use sys_traits::EnvCurrentDir; @@ -52,8 +51,8 @@ pub trait CliCodeCache: code_cache::CodeCache { #[async_trait::async_trait(?Send)] pub trait CoverageCollector: Send + Sync { - async fn start_collecting(&mut self) -> Result<(), AnyError>; - async fn stop_collecting(&mut self) -> Result<(), AnyError>; + async fn start_collecting(&mut self) -> Result<(), CoreError>; + async fn stop_collecting(&mut self) -> Result<(), CoreError>; } pub type CreateHmrRunnerCb = Box< @@ -91,7 +90,7 @@ impl CliMainWorker { self.worker.into_main_worker() } - pub async fn setup_repl(&mut self) -> Result<(), AnyError> { + pub async fn setup_repl(&mut self) -> Result<(), CoreError> { self.worker.run_event_loop(false).await?; Ok(()) } @@ -172,7 +171,7 @@ impl CliMainWorker { Ok(self.worker.exit_code()) } - pub async fn run_for_watcher(self) -> Result<(), AnyError> { + pub async fn run_for_watcher(self) -> Result<(), CoreError> { /// The FileWatcherModuleExecutor provides module execution with safe dispatching of life-cycle events by tracking the /// state of any pending events and emitting accordingly on drop in the case of a future /// cancellation. @@ -191,7 +190,7 @@ impl CliMainWorker { /// Execute the given main module emitting load and unload events before and after execution /// respectively. - pub async fn execute(&mut self) -> Result<(), AnyError> { + pub async fn execute(&mut self) -> Result<(), CoreError> { self.inner.execute_main_module().await?; self.inner.worker.dispatch_load_event()?; self.pending_unload = true; @@ -245,7 +244,7 @@ impl CliMainWorker { pub async fn maybe_setup_hmr_runner( &mut self, - ) -> Result>, AnyError> { + ) -> Result>, CoreError> { let Some(setup_hmr_runner) = self.shared.create_hmr_runner.as_ref() else { return Ok(None); }; @@ -267,7 +266,7 @@ impl CliMainWorker { pub async fn maybe_setup_coverage_collector( &mut self, - ) -> Result>, AnyError> { + ) -> Result>, CoreError> { let Some(create_coverage_collector) = self.shared.create_coverage_collector.as_ref() else { @@ -296,6 +295,58 @@ impl CliMainWorker { } } +#[derive(Debug, thiserror::Error, deno_error::JsError)] +pub enum ResolveBinaryEntrypointError { + #[class(inherit)] + #[error(transparent)] + ResolvePkgJsonBinExport(ResolvePkgJsonBinExportError), + #[class(generic)] + #[error("{original:#}\n\nFallback failed: {fallback:#}")] + Fallback { + fallback: ResolveBinaryEntrypointFallbackError, + original: ResolvePkgJsonBinExportError, + }, +} + +#[derive(Debug, thiserror::Error, deno_error::JsError)] +pub enum ResolveBinaryEntrypointFallbackError { + #[class(inherit)] + #[error(transparent)] + PackageSubpathResolve(node_resolver::errors::PackageSubpathResolveError), + #[class(generic)] + #[error("Cannot find module '{0}'")] + ModuleNotFound(ModuleSpecifier), +} + +#[derive(Debug, thiserror::Error, deno_error::JsError)] +pub enum CreateCustomWorkerError { + #[class(inherit)] + #[error(transparent)] + Io(#[from] std::io::Error), + #[class(inherit)] + #[error(transparent)] + Core(#[from] CoreError), + #[class(inherit)] + #[error(transparent)] + ResolvePkgFolderFromDenoReq( + #[from] deno_resolver::npm::ResolvePkgFolderFromDenoReqError, + ), + #[class(inherit)] + #[error(transparent)] + UrlParse(#[from] deno_core::url::ParseError), + #[class(inherit)] + #[error(transparent)] + ResolveBinaryEntrypoint(#[from] ResolveBinaryEntrypointError), + #[class(inherit)] + #[error(transparent)] + NpmPackageReq(JsErrorBox), + #[class(inherit)] + #[error(transparent)] + AtomicWriteFileWithRetries( + #[from] crate::args::AtomicWriteFileWithRetriesError, + ), +} + pub struct CliMainWorkerFactory { lib_main_worker_factory: LibMainWorkerFactory, maybe_lockfile: Option>, @@ -344,7 +395,7 @@ impl CliMainWorkerFactory { &self, mode: WorkerExecutionMode, main_module: ModuleSpecifier, - ) -> Result { + ) -> Result { self .create_custom_worker( mode, @@ -363,7 +414,7 @@ impl CliMainWorkerFactory { permissions: PermissionsContainer, custom_extensions: Vec, stdio: deno_runtime::deno_io::Stdio, - ) -> Result { + ) -> Result { let main_module = if let Ok(package_ref) = NpmPackageReqReference::from_specifier(&main_module) { @@ -381,19 +432,20 @@ impl CliMainWorkerFactory { PackageCaching::All }, ) - .await?; + .await + .map_err(CreateCustomWorkerError::NpmPackageReq)?; } // use a fake referrer that can be used to discover the package.json if necessary - let referrer = ModuleSpecifier::from_directory_path( - self.sys.env_current_dir().map_err(JsErrorBox::from_err)?, - ) - .unwrap() - .join("package.json")?; - let package_folder = self - .npm_resolver - .resolve_pkg_folder_from_deno_module_req(package_ref.req(), &referrer) - .map_err(JsErrorBox::from_err)?; + let referrer = + ModuleSpecifier::from_directory_path(self.sys.env_current_dir()?) + .unwrap() + .join("package.json")?; + let package_folder = + self.npm_resolver.resolve_pkg_folder_from_deno_module_req( + package_ref.req(), + &referrer, + )?; let main_module = self .resolve_binary_entrypoint(&package_folder, package_ref.sub_path())?; @@ -447,7 +499,7 @@ impl CliMainWorkerFactory { &self, package_folder: &Path, sub_path: Option<&str>, - ) -> Result { + ) -> Result { match self .node_resolver .resolve_binary_export(package_folder, sub_path) @@ -459,10 +511,13 @@ impl CliMainWorkerFactory { self.resolve_binary_entrypoint_fallback(package_folder, sub_path); match result { Ok(Some(specifier)) => Ok(specifier), - Ok(None) => Err(original_err.into()), - Err(fallback_err) => { - bail!("{:#}\n\nFallback failed: {:#}", original_err, fallback_err) - } + Ok(None) => Err( + ResolveBinaryEntrypointError::ResolvePkgJsonBinExport(original_err), + ), + Err(fallback_err) => Err(ResolveBinaryEntrypointError::Fallback { + original: original_err, + fallback: fallback_err, + }), } } } @@ -473,7 +528,7 @@ impl CliMainWorkerFactory { &self, package_folder: &Path, sub_path: Option<&str>, - ) -> Result, AnyError> { + ) -> Result, ResolveBinaryEntrypointFallbackError> { // 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 @@ -490,7 +545,8 @@ impl CliMainWorkerFactory { /* referrer */ None, ResolutionMode::Import, NodeResolutionKind::Execution, - )?; + ) + .map_err(ResolveBinaryEntrypointFallbackError::PackageSubpathResolve)?; if specifier .to_file_path() .map(|p| p.exists()) @@ -498,7 +554,9 @@ impl CliMainWorkerFactory { { Ok(Some(specifier)) } else { - bail!("Cannot find module '{}'", specifier) + Err(ResolveBinaryEntrypointFallbackError::ModuleNotFound( + specifier, + )) } } } diff --git a/ext/net/quic.rs b/ext/net/quic.rs index e075745495..af13a3f009 100644 --- a/ext/net/quic.rs +++ b/ext/net/quic.rs @@ -99,9 +99,6 @@ pub enum QuicError { #[class(range)] #[error("Connection has reached the maximum number of concurrent outgoing {0} streams")] MaxStreams(&'static str), - #[class(generic)] - #[error("{0}")] - Core(#[from] deno_core::error::AnyError), #[class(inherit)] #[error(transparent)] Other(#[from] JsErrorBox), diff --git a/ext/node/ops/require.rs b/ext/node/ops/require.rs index bff0cd79ed..c0e54993ae 100644 --- a/ext/node/ops/require.rs +++ b/ext/node/ops/require.rs @@ -106,10 +106,15 @@ pub enum RequireErrorKind { JsErrorBox, ), #[class(inherit)] - #[error("Unable to get CWD: {0}")] - UnableToGetCwd(#[inherit] std::io::Error), + #[error(transparent)] + UnableToGetCwd(UnableToGetCwdError), } +#[derive(Debug, thiserror::Error, deno_error::JsError)] +#[error("Unable to get CWD")] +#[class(inherit)] +pub struct UnableToGetCwdError(#[source] pub std::io::Error); + #[op2] #[serde] pub fn op_require_init_paths() -> Vec { @@ -177,7 +182,7 @@ pub fn op_require_node_module_paths< } else { let current_dir = &sys .env_current_dir() - .map_err(RequireErrorKind::UnableToGetCwd)?; + .map_err(|e| RequireErrorKind::UnableToGetCwd(UnableToGetCwdError(e)))?; normalize_path(current_dir.join(from)) }; diff --git a/resolvers/node/analyze.rs b/resolvers/node/analyze.rs index e144e2b8fb..a67023d1e1 100644 --- a/resolvers/node/analyze.rs +++ b/resolvers/node/analyze.rs @@ -6,8 +6,7 @@ use std::collections::HashSet; use std::path::Path; use std::path::PathBuf; -use anyhow::Context; -use anyhow::Error as AnyError; +use deno_error::JsErrorBox; use deno_path_util::url_from_file_path; use deno_path_util::url_to_file_path; use futures::future::LocalBoxFuture; @@ -43,6 +42,16 @@ pub struct CjsAnalysisExports { pub reexports: Vec, } +#[derive(Debug, thiserror::Error, deno_error::JsError)] +pub enum CjsCodeAnalysisError { + #[class(inherit)] + #[error(transparent)] + ClosestPkgJson(#[from] crate::errors::ClosestPkgJsonError), + #[class(inherit)] + #[error(transparent)] + Other(#[from] JsErrorBox), +} + /// Code analyzer for CJS and ESM files. #[async_trait::async_trait(?Send)] pub trait CjsCodeAnalyzer { @@ -57,7 +66,28 @@ pub trait CjsCodeAnalyzer { &self, specifier: &Url, maybe_source: Option>, - ) -> Result, AnyError>; + ) -> Result, CjsCodeAnalysisError>; +} + +#[derive(Debug, thiserror::Error, deno_error::JsError)] +pub enum TranslateCjsToEsmError { + #[class(inherit)] + #[error(transparent)] + CjsCodeAnalysis(#[from] CjsCodeAnalysisError), + #[class(inherit)] + #[error(transparent)] + ExportAnalysis(#[from] JsErrorBox), +} + +#[derive(Debug, thiserror::Error, deno_error::JsError)] +#[class(generic)] +#[error("Could not load '{reexport}' ({reexport_specifier}) referenced from {referrer}")] +pub struct CjsAnalysisCouldNotLoadError { + reexport: String, + reexport_specifier: Url, + referrer: Url, + #[source] + source: CjsCodeAnalysisError, } pub struct NodeCodeTranslator< @@ -128,7 +158,7 @@ impl< &self, entry_specifier: &Url, source: Option>, - ) -> Result, AnyError> { + ) -> Result, TranslateCjsToEsmError> { let mut temp_var_count = 0; let analysis = self @@ -164,7 +194,7 @@ impl< // surface errors afterwards in a deterministic way if !errors.is_empty() { errors.sort_by_cached_key(|e| e.to_string()); - return Err(errors.remove(0)); + return Err(TranslateCjsToEsmError::ExportAnalysis(errors.remove(0))); } } @@ -208,7 +238,7 @@ impl< all_exports: &mut BTreeSet, // this goes through the modules concurrently, so collect // the errors in order to be deterministic - errors: &mut Vec, + errors: &mut Vec, ) { struct Analysis { reexport_specifier: url::Url, @@ -216,7 +246,7 @@ impl< analysis: CjsAnalysis<'static>, } - type AnalysisFuture<'a> = LocalBoxFuture<'a, Result>; + type AnalysisFuture<'a> = LocalBoxFuture<'a, Result>; let mut handled_reexports: HashSet = HashSet::default(); handled_reexports.insert(entry_specifier.clone()); @@ -227,7 +257,7 @@ impl< |referrer: url::Url, reexports: Vec, analyze_futures: &mut FuturesUnordered>, - errors: &mut Vec| { + errors: &mut Vec| { // 1. Resolve the re-exports and start a future to analyze each one for reexport in reexports { let result = self.resolve( @@ -256,11 +286,13 @@ impl< let analysis = cjs_code_analyzer .analyze_cjs(&reexport_specifier, None) .await - .with_context(|| { - format!( - "Could not load '{}' ({}) referenced from {}", - reexport, reexport_specifier, referrer - ) + .map_err(|source| { + JsErrorBox::from_err(CjsAnalysisCouldNotLoadError { + reexport, + reexport_specifier: reexport_specifier.clone(), + referrer: referrer.clone(), + source, + }) })?; Ok(Analysis { @@ -297,11 +329,10 @@ impl< match analysis { CjsAnalysis::Esm(_) => { // todo(dsherret): support this once supporting requiring ES modules - errors.push(anyhow::anyhow!( + errors.push(JsErrorBox::generic(format!( "Cannot require ES module '{}' from '{}'", - reexport_specifier, - referrer, - )); + reexport_specifier, referrer, + ))); } CjsAnalysis::Cjs(analysis) => { if !analysis.reexports.is_empty() { @@ -331,7 +362,7 @@ impl< referrer: &Url, conditions: &[&str], resolution_kind: NodeResolutionKind, - ) -> Result, AnyError> { + ) -> Result, JsErrorBox> { if specifier.starts_with('/') { todo!(); } @@ -341,7 +372,7 @@ impl< 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(AnyError::from)) + .and_then(|p| url_from_file_path(&p).map_err(JsErrorBox::from_err)) .map(Some); } else { todo!(); @@ -364,13 +395,14 @@ impl< { return Ok(None); } - other => other, - }?; + other => other.map_err(JsErrorBox::from_err)?, + }; let package_json_path = module_dir.join("package.json"); let maybe_package_json = self .pkg_json_resolver - .load_package_json(&package_json_path)?; + .load_package_json(&package_json_path) + .map_err(JsErrorBox::from_err)?; if let Some(package_json) = maybe_package_json { if let Some(exports) = &package_json.exports { return Some( @@ -385,7 +417,7 @@ impl< conditions, resolution_kind, ) - .map_err(AnyError::from), + .map_err(JsErrorBox::from_err), ) .transpose(); } @@ -398,29 +430,40 @@ impl< let package_json_path = d.join("package.json"); let maybe_package_json = self .pkg_json_resolver - .load_package_json(&package_json_path)?; + .load_package_json(&package_json_path) + .map_err(JsErrorBox::from_err)?; if let Some(package_json) = maybe_package_json { if let Some(main) = package_json.main(deno_package_json::NodeModuleKind::Cjs) { - return Ok(Some(url_from_file_path(&d.join(main).clean())?)); + return Ok(Some( + url_from_file_path(&d.join(main).clean()) + .map_err(JsErrorBox::from_err)?, + )); } } - return Ok(Some(url_from_file_path(&d.join("index.js").clean())?)); + return Ok(Some( + url_from_file_path(&d.join("index.js").clean()) + .map_err(JsErrorBox::from_err)?, + )); } return self .file_extension_probe(d, &referrer_path) - .and_then(|p| url_from_file_path(&p).map_err(AnyError::from)) + .and_then(|p| url_from_file_path(&p).map_err(JsErrorBox::from_err)) .map(Some); } 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())?)); + return Ok(Some( + url_from_file_path(&module_dir.join(main).clean()) + .map_err(JsErrorBox::from_err)?, + )); } else { - return Ok(Some(url_from_file_path( - &module_dir.join("index.js").clean(), - )?)); + return Ok(Some( + url_from_file_path(&module_dir.join("index.js").clean()) + .map_err(JsErrorBox::from_err)?, + )); } } @@ -436,7 +479,9 @@ impl< 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)?)); + return Ok(Some( + url_from_file_path(&path).map_err(JsErrorBox::from_err)?, + )); } last = parent; } @@ -448,7 +493,7 @@ impl< &self, p: PathBuf, referrer: &Path, - ) -> Result { + ) -> Result { let p = p.clean(); if self.sys.fs_exists_no_err(&p) { let file_name = p.file_name().unwrap(); @@ -638,13 +683,13 @@ fn parse_specifier(specifier: &str) -> Option<(String, String)> { Some((package_name, package_subpath)) } -fn not_found(path: &str, referrer: &Path) -> AnyError { +fn not_found(path: &str, referrer: &Path) -> JsErrorBox { let msg = format!( "[ERR_MODULE_NOT_FOUND] Cannot find module \"{}\" imported from \"{}\"", path, referrer.to_string_lossy() ); - std::io::Error::new(std::io::ErrorKind::NotFound, msg).into() + JsErrorBox::from_err(std::io::Error::new(std::io::ErrorKind::NotFound, msg)) } fn to_double_quote_string(text: &str) -> String { diff --git a/runtime/fs_util.rs b/runtime/fs_util.rs index 7788a97170..5dca3a55ee 100644 --- a/runtime/fs_util.rs +++ b/runtime/fs_util.rs @@ -4,11 +4,12 @@ use std::path::Path; use std::path::PathBuf; use deno_core::anyhow::Context; -use deno_core::error::AnyError; use deno_path_util::normalize_path; #[inline] -pub fn resolve_from_cwd(path: &Path) -> Result { +pub fn resolve_from_cwd( + path: &Path, +) -> Result { if path.is_absolute() { Ok(normalize_path(path)) } else { diff --git a/runtime/inspector_server.rs b/runtime/inspector_server.rs index 75e9668db4..cf043c97ac 100644 --- a/runtime/inspector_server.rs +++ b/runtime/inspector_server.rs @@ -10,8 +10,6 @@ use std::process; use std::rc::Rc; use std::thread; -use deno_core::anyhow::Context; -use deno_core::error::AnyError; use deno_core::futures::channel::mpsc; use deno_core::futures::channel::mpsc::UnboundedReceiver; use deno_core::futures::channel::mpsc::UnboundedSender; @@ -49,17 +47,33 @@ pub struct InspectorServer { thread_handle: Option>, } +#[derive(Debug, thiserror::Error, deno_error::JsError)] +pub enum InspectorServerError { + #[class(inherit)] + #[error(transparent)] + Io(#[from] std::io::Error), + #[class(inherit)] + #[error("Failed to start inspector server at \"{host}\"")] + Connect { + host: SocketAddr, + #[source] + #[inherit] + source: std::io::Error, + }, +} + impl InspectorServer { - pub fn new(host: SocketAddr, name: &'static str) -> Result { + pub fn new( + host: SocketAddr, + name: &'static str, + ) -> Result { let (register_inspector_tx, register_inspector_rx) = mpsc::unbounded::(); let (shutdown_server_tx, shutdown_server_rx) = broadcast::channel(1); - let tcp_listener = - std::net::TcpListener::bind(host).with_context(|| { - format!("Failed to start inspector server at \"{}\"", host) - })?; + let tcp_listener = std::net::TcpListener::bind(host) + .map_err(|source| InspectorServerError::Connect { host, source })?; tcp_listener.set_nonblocking(true)?; let thread_handle = thread::spawn(move || {