From a5eb2dfc93afc2899ed6e1ad2b3e029157889f7c Mon Sep 17 00:00:00 2001 From: Kitson Kelly Date: Tue, 22 Jun 2021 07:27:32 +1000 Subject: [PATCH] fix(#10761): graph errors reported as diagnostics for `Deno.emit()` (#10767) Fixes #10761 --- cli/diagnostics.rs | 33 +++++++++++++++++++----- cli/module_graph.rs | 12 +++++++++ cli/ops/runtime_compiler.rs | 4 ++- cli/tests/compiler_api_test.ts | 46 ++++++++++++++++++++++++++++++++++ 4 files changed, 88 insertions(+), 7 deletions(-) diff --git a/cli/diagnostics.rs b/cli/diagnostics.rs index b7327d72c9..695330af32 100644 --- a/cli/diagnostics.rs +++ b/cli/diagnostics.rs @@ -6,7 +6,9 @@ use deno_core::serde::Deserialize; use deno_core::serde::Deserializer; use deno_core::serde::Serialize; use deno_core::serde::Serializer; +use deno_core::ModuleSpecifier; use regex::Regex; +use std::collections::HashMap; use std::error::Error; use std::fmt; @@ -233,13 +235,14 @@ impl Diagnostic { _ => "", }; + let code = if self.code >= 900001 { + "".to_string() + } else { + colors::bold(format!("TS{} ", self.code)).to_string() + }; + if !category.is_empty() { - write!( - f, - "{} [{}]: ", - colors::bold(&format!("TS{}", self.code)), - category - ) + write!(f, "{}[{}]: ", code, category) } else { Ok(()) } @@ -359,6 +362,24 @@ impl Diagnostics { Diagnostics(diagnostics) } + pub fn extend_graph_errors( + &mut self, + errors: HashMap, + ) { + self.0.extend(errors.into_iter().map(|(s, e)| Diagnostic { + category: DiagnosticCategory::Error, + code: 900001, + start: None, + end: None, + message_text: Some(e), + message_chain: None, + source: None, + source_line: None, + file_name: Some(s.to_string()), + related_information: None, + })); + } + pub fn is_empty(&self) -> bool { self.0.is_empty() } diff --git a/cli/module_graph.rs b/cli/module_graph.rs index 8ea8b7f886..e56f26c156 100644 --- a/cli/module_graph.rs +++ b/cli/module_graph.rs @@ -1273,6 +1273,18 @@ impl Graph { Ok(()) } + /// Retrieve the first module loading error from the graph and return it. + pub fn get_errors(&self) -> HashMap { + self + .modules + .iter() + .filter_map(|(s, sl)| match sl { + ModuleSlot::Err(err) => Some((s.clone(), err.to_string())), + _ => None, + }) + .collect() + } + /// Retrieve a map that contains a representation of each module in the graph /// which can be used to provide code to a module loader without holding all /// the state to be able to operate on the graph. diff --git a/cli/ops/runtime_compiler.rs b/cli/ops/runtime_compiler.rs index 099f2d5559..dd500077ae 100644 --- a/cli/ops/runtime_compiler.rs +++ b/cli/ops/runtime_compiler.rs @@ -118,12 +118,14 @@ async fn op_emit( }; let graph = builder.get_graph(); let debug = program_state.flags.log_level == Some(log::Level::Debug); - let (files, result_info) = graph.emit(EmitOptions { + let graph_errors = graph.get_errors(); + let (files, mut result_info) = graph.emit(EmitOptions { bundle_type, check: args.check.unwrap_or(true), debug, maybe_user_config: args.compiler_options, })?; + result_info.diagnostics.extend_graph_errors(graph_errors); Ok(json!({ "diagnostics": result_info.diagnostics, diff --git a/cli/tests/compiler_api_test.ts b/cli/tests/compiler_api_test.ts index b9a08d5caf..2e0a8820c3 100644 --- a/cli/tests/compiler_api_test.ts +++ b/cli/tests/compiler_api_test.ts @@ -456,3 +456,49 @@ Deno.test({ assert(files["deno:///bundle.js.map"]); }, }); + +Deno.test({ + name: `Deno.emit() - graph errors as diagnostics`, + ignore: Deno.build.os === "windows", + async fn() { + const { diagnostics } = await Deno.emit("/a.ts", { + sources: { + "/a.ts": `import { b } from "./b.ts"; + console.log(b);`, + }, + }); + assert(diagnostics); + assertEquals(diagnostics, [ + { + category: 1, + code: 2305, + start: { line: 0, character: 9 }, + end: { line: 0, character: 10 }, + messageText: + `Module '"deno:///missing_dependency.d.ts"' has no exported member 'b'.`, + messageChain: null, + source: null, + sourceLine: 'import { b } from "./b.ts";', + fileName: "file:///a.ts", + relatedInformation: null, + }, + { + category: 1, + code: 900001, + start: null, + end: null, + messageText: "Unable to find specifier in sources: file:///b.ts", + messageChain: null, + source: null, + sourceLine: null, + fileName: "file:///b.ts", + relatedInformation: null, + }, + ]); + assert( + Deno.formatDiagnostics(diagnostics).includes( + "Unable to find specifier in sources: file:///b.ts", + ), + ); + }, +});