summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKitson Kelly <me@kitsonkelly.com>2020-09-12 19:53:57 +1000
committerGitHub <noreply@github.com>2020-09-12 11:53:57 +0200
commit10fbfcbc79eb50cb7669b4aaf67f957d97d8d93b (patch)
tree99bbe8fcf2bb554f2203d990252556924e1cc18c
parent5276cc85923a1791bf73a91b05c27fbdeeaa6f9b (diff)
refactor: improve tsc diagnostics (#7420)
-rw-r--r--cli/diagnostics.rs898
-rw-r--r--cli/dts/lib.deno.unstable.d.ts54
-rw-r--r--cli/ops/errors.rs4
-rw-r--r--cli/rt/40_diagnostics.js20
-rw-r--r--cli/rt/40_error_stack.js4
-rw-r--r--cli/tests/unit/format_error_test.ts30
-rw-r--r--cli/tsc.rs17
-rw-r--r--cli/tsc/99_main_compiler.js298
8 files changed, 647 insertions, 678 deletions
diff --git a/cli/diagnostics.rs b/cli/diagnostics.rs
index 41ee5ec22..083c11bf5 100644
--- a/cli/diagnostics.rs
+++ b/cli/diagnostics.rs
@@ -1,451 +1,625 @@
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
-//! This module encodes TypeScript errors (diagnostics) into Rust structs and
-//! contains code for printing them to the console.
use crate::colors;
-use crate::fmt_errors::format_stack;
+
+use regex::Regex;
use serde::Deserialize;
use serde::Deserializer;
use std::error::Error;
use std::fmt;
-#[derive(Clone, Debug, Deserialize, PartialEq)]
-#[serde(rename_all = "camelCase")]
-pub struct Diagnostic {
- pub items: Vec<DiagnosticItem>,
+const MAX_SOURCE_LINE_LENGTH: usize = 150;
+
+const UNSTABLE_DENO_PROPS: &[&str] = &[
+ "CompilerOptions",
+ "DatagramConn",
+ "Diagnostic",
+ "DiagnosticCategory",
+ "DiagnosticItem",
+ "DiagnosticMessageChain",
+ "EnvPermissionDescriptor",
+ "HrtimePermissionDescriptor",
+ "HttpClient",
+ "LinuxSignal",
+ "Location",
+ "MacOSSignal",
+ "NetPermissionDescriptor",
+ "PermissionDescriptor",
+ "PermissionName",
+ "PermissionState",
+ "PermissionStatus",
+ "Permissions",
+ "PluginPermissionDescriptor",
+ "ReadPermissionDescriptor",
+ "RunPermissionDescriptor",
+ "ShutdownMode",
+ "Signal",
+ "SignalStream",
+ "StartTlsOptions",
+ "SymlinkOptions",
+ "TranspileOnlyResult",
+ "UnixConnectOptions",
+ "UnixListenOptions",
+ "WritePermissionDescriptor",
+ "applySourceMap",
+ "bundle",
+ "compile",
+ "connect",
+ "consoleSize",
+ "createHttpClient",
+ "fdatasync",
+ "fdatasyncSync",
+ "formatDiagnostics",
+ "futime",
+ "futimeSync",
+ "fstat",
+ "fstatSync",
+ "fsync",
+ "fsyncSync",
+ "ftruncate",
+ "ftruncateSync",
+ "hostname",
+ "kill",
+ "link",
+ "linkSync",
+ "listen",
+ "listenDatagram",
+ "loadavg",
+ "mainModule",
+ "openPlugin",
+ "osRelease",
+ "permissions",
+ "ppid",
+ "setRaw",
+ "shutdown",
+ "signal",
+ "signals",
+ "startTls",
+ "symlink",
+ "symlinkSync",
+ "transpileOnly",
+ "umask",
+ "utime",
+ "utimeSync",
+];
+
+lazy_static! {
+ static ref MSG_MISSING_PROPERTY_DENO: Regex =
+ Regex::new(r#"Property '([^']+)' does not exist on type 'typeof Deno'"#)
+ .unwrap();
+ static ref MSG_SUGGESTION: Regex =
+ Regex::new(r#" Did you mean '([^']+)'\?"#).unwrap();
}
-impl fmt::Display for Diagnostic {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- let mut i = 0;
- for item in &self.items {
- if i > 0 {
- write!(f, "\n\n")?;
+/// Potentially convert a "raw" diagnostic message from TSC to something that
+/// provides a more sensible error message given a Deno runtime context.
+fn format_message(msg: &str, code: &u64) -> String {
+ match code {
+ 2339 => {
+ if let Some(captures) = MSG_MISSING_PROPERTY_DENO.captures(msg) {
+ if let Some(property) = captures.get(1) {
+ if UNSTABLE_DENO_PROPS.contains(&property.as_str()) {
+ return format!("{} 'Deno.{}' is an unstable API. Did you forget to run with the '--unstable' flag?", msg, property.as_str());
+ }
+ }
}
- write!(f, "{}", item.to_string())?;
- i += 1;
- }
- if i > 1 {
- write!(f, "\n\nFound {} errors.", i)?;
+ msg.to_string()
}
+ 2551 => {
+ if let (Some(caps_property), Some(caps_suggestion)) = (
+ MSG_MISSING_PROPERTY_DENO.captures(msg),
+ MSG_SUGGESTION.captures(msg),
+ ) {
+ if let (Some(property), Some(suggestion)) =
+ (caps_property.get(1), caps_suggestion.get(1))
+ {
+ if UNSTABLE_DENO_PROPS.contains(&property.as_str()) {
+ return format!("{} 'Deno.{}' is an unstable API. Did you forget to run with the '--unstable' flag, or did you mean '{}'?", MSG_SUGGESTION.replace(msg, ""), property.as_str(), suggestion.as_str());
+ }
+ }
+ }
- Ok(())
+ msg.to_string()
+ }
+ _ => msg.to_string(),
}
}
-impl Error for Diagnostic {
- fn description(&self) -> &str {
- &self.items[0].message
- }
+#[derive(Clone, Debug, PartialEq)]
+pub enum DiagnosticCategory {
+ Warning,
+ Error,
+ Suggestion,
+ Message,
}
-#[derive(Clone, Debug, Deserialize, PartialEq)]
-#[serde(rename_all = "camelCase")]
-pub struct DiagnosticItem {
- /// The top level message relating to the diagnostic item.
- pub message: String,
-
- /// A chain of messages, code, and categories of messages which indicate the
- /// full diagnostic information.
- pub message_chain: Option<DiagnosticMessageChain>,
-
- /// Other diagnostic items that are related to the diagnostic, usually these
- /// are suggestions of why an error occurred.
- pub related_information: Option<Vec<DiagnosticItem>>,
-
- /// The source line the diagnostic is in reference to.
- pub source_line: Option<String>,
-
- /// Zero-based index to the line number of the error.
- pub line_number: Option<i64>,
-
- /// The resource name provided to the TypeScript compiler.
- pub script_resource_name: Option<String>,
-
- /// Zero-based index to the start position in the entire script resource.
- pub start_position: Option<i64>,
-
- /// Zero-based index to the end position in the entire script resource.
- pub end_position: Option<i64>,
- pub category: DiagnosticCategory,
-
- /// This is defined in TypeScript and can be referenced via
- /// [diagnosticMessages.json](https://github.com/microsoft/TypeScript/blob/master/src/compiler/diagnosticMessages.json).
- pub code: i64,
+impl fmt::Display for DiagnosticCategory {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(
+ f,
+ "{}",
+ match self {
+ DiagnosticCategory::Warning => "WARN ",
+ DiagnosticCategory::Error => "ERROR ",
+ DiagnosticCategory::Suggestion => "",
+ DiagnosticCategory::Message => "",
+ }
+ )
+ }
+}
- /// Zero-based index to the start column on `line_number`.
- pub start_column: Option<i64>,
+impl<'de> Deserialize<'de> for DiagnosticCategory {
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+ where
+ D: Deserializer<'de>,
+ {
+ let s: i64 = Deserialize::deserialize(deserializer)?;
+ Ok(DiagnosticCategory::from(s))
+ }
+}
- /// Zero-based index to the end column on `line_number`.
- pub end_column: Option<i64>,
+impl From<i64> for DiagnosticCategory {
+ fn from(value: i64) -> Self {
+ match value {
+ 0 => DiagnosticCategory::Warning,
+ 1 => DiagnosticCategory::Error,
+ 2 => DiagnosticCategory::Suggestion,
+ 3 => DiagnosticCategory::Message,
+ _ => panic!("Unknown value: {}", value),
+ }
+ }
}
-fn format_category_and_code(
- category: &DiagnosticCategory,
+#[derive(Debug, Deserialize, Clone)]
+#[serde(rename_all = "camelCase")]
+pub struct DiagnosticMessageChain {
+ message_text: String,
+ category: DiagnosticCategory,
code: i64,
-) -> String {
- let category = match category {
- DiagnosticCategory::Error => "ERROR".to_string(),
- DiagnosticCategory::Warning => "WARN".to_string(),
- DiagnosticCategory::Debug => "DEBUG".to_string(),
- DiagnosticCategory::Info => "INFO".to_string(),
- _ => "".to_string(),
- };
-
- let code = colors::bold(&format!("TS{}", code.to_string())).to_string();
-
- format!("{} [{}]", code, category)
+ next: Option<Vec<DiagnosticMessageChain>>,
}
-fn format_message(
- message_chain: &Option<DiagnosticMessageChain>,
- message: &str,
- level: usize,
-) -> String {
- debug!("format_message");
+impl DiagnosticMessageChain {
+ pub fn format_message(&self, level: usize) -> String {
+ let mut s = String::new();
- if let Some(message_chain) = message_chain {
- let mut s = message_chain.format_message(level);
- s.pop();
+ s.push_str(&std::iter::repeat(" ").take(level * 2).collect::<String>());
+ s.push_str(&self.message_text);
+ if let Some(next) = &self.next {
+ s.push('\n');
+ let arr = next.clone();
+ for dm in arr {
+ s.push_str(&dm.format_message(level + 1));
+ }
+ }
s
- } else {
- format!("{:indent$}{}", "", message, indent = level)
}
}
-/// Formats optional source, line and column numbers into a single string.
-fn format_maybe_frame(
- file_name: Option<&str>,
- line_number: Option<i64>,
- column_number: Option<i64>,
-) -> String {
- if file_name.is_none() {
- return "".to_string();
- }
-
- assert!(line_number.is_some());
- assert!(column_number.is_some());
+#[derive(Debug, Deserialize, Clone)]
+#[serde(rename_all = "camelCase")]
+pub struct Position {
+ pub line: u64,
+ pub character: u64,
+}
- let line_number = line_number.unwrap();
- let column_number = column_number.unwrap();
- let file_name_c = colors::cyan(file_name.unwrap());
- let line_c = colors::yellow(&line_number.to_string());
- let column_c = colors::yellow(&column_number.to_string());
- format!("{}:{}:{}", file_name_c, line_c, column_c)
+#[derive(Debug, Deserialize, Clone)]
+#[serde(rename_all = "camelCase")]
+pub struct Diagnostic {
+ category: DiagnosticCategory,
+ code: u64,
+ start: Option<Position>,
+ end: Option<Position>,
+ message_text: Option<String>,
+ message_chain: Option<DiagnosticMessageChain>,
+ source: Option<String>,
+ source_line: Option<String>,
+ file_name: Option<String>,
+ related_information: Option<Vec<Diagnostic>>,
}
-fn format_maybe_related_information(
- related_information: &Option<Vec<DiagnosticItem>>,
-) -> String {
- if related_information.is_none() {
- return "".to_string();
+impl Diagnostic {
+ fn fmt_category_and_code(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ let category = match self.category {
+ DiagnosticCategory::Error => "ERROR",
+ DiagnosticCategory::Warning => "WARN",
+ _ => "",
+ };
+
+ if !category.is_empty() {
+ write!(
+ f,
+ "{} [{}]: ",
+ colors::bold(&format!("TS{}", self.code)),
+ category
+ )
+ } else {
+ Ok(())
+ }
}
- let mut s = String::new();
-
- if let Some(related_information) = related_information {
- for rd in related_information {
- s.push_str("\n\n");
- s.push_str(&format_stack(
- matches!(rd.category, DiagnosticCategory::Error),
- &format_message(&rd.message_chain, &rd.message, 0),
- rd.source_line.as_deref(),
- rd.start_column,
- rd.end_column,
- // Formatter expects 1-based line and column numbers, but ours are 0-based.
- &[format_maybe_frame(
- rd.script_resource_name.as_deref(),
- rd.line_number.map(|n| n + 1),
- rd.start_column.map(|n| n + 1),
- )],
- 4,
- ));
+ fn fmt_frame(&self, f: &mut fmt::Formatter, level: usize) -> fmt::Result {
+ if let (Some(file_name), Some(start)) =
+ (self.file_name.as_ref(), self.start.as_ref())
+ {
+ write!(
+ f,
+ "\n{:indent$} at {}:{}:{}",
+ "",
+ colors::cyan(file_name),
+ colors::yellow(&(start.line + 1).to_string()),
+ colors::yellow(&(start.character + 1).to_string()),
+ indent = level
+ )
+ } else {
+ Ok(())
}
}
- s
-}
-
-impl fmt::Display for DiagnosticItem {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- write!(
- f,
- "{}",
- format_stack(
- matches!(self.category, DiagnosticCategory::Error),
- &format!(
- "{}: {}",
- format_category_and_code(&self.category, self.code),
- format_message(&self.message_chain, &self.message, 0)
- ),
- self.source_line.as_deref(),
- self.start_column,
- self.end_column,
- // Formatter expects 1-based line and column numbers, but ours are 0-based.
- &[format_maybe_frame(
- self.script_resource_name.as_deref(),
- self.line_number.map(|n| n + 1),
- self.start_column.map(|n| n + 1)
- )],
- 0
+ fn fmt_message(&self, f: &mut fmt::Formatter, level: usize) -> fmt::Result {
+ if let Some(message_chain) = &self.message_chain {
+ write!(f, "{}", message_chain.format_message(level))
+ } else {
+ write!(
+ f,
+ "{:indent$}{}",
+ "",
+ format_message(&self.message_text.clone().unwrap(), &self.code),
+ indent = level,
)
- )?;
- write!(
- f,
- "{}",
- format_maybe_related_information(&self.related_information),
- )
+ }
}
-}
-#[derive(Clone, Debug, Deserialize, PartialEq)]
-#[serde(rename_all = "camelCase")]
-pub struct DiagnosticMessageChain {
- pub message: String,
- pub code: i64,
- pub category: DiagnosticCategory,
- pub next: Option<Vec<DiagnosticMessageChain>>,
-}
+ fn fmt_source_line(
+ &self,
+ f: &mut fmt::Formatter,
+ level: usize,
+ ) -> fmt::Result {
+ if let (Some(source_line), Some(start), Some(end)) =
+ (&self.source_line, &self.start, &self.end)
+ {
+ if !source_line.is_empty() && source_line.len() <= MAX_SOURCE_LINE_LENGTH
+ {
+ write!(f, "\n{:indent$}{}", "", source_line, indent = level)?;
+ let length = if start.line == end.line {
+ end.character - start.character
+ } else {
+ 1
+ };
+ let mut s = String::new();
+ for i in 0..start.character {
+ s.push(if source_line.chars().nth(i as usize).unwrap() == '\t' {
+ '\t'
+ } else {
+ ' '
+ });
+ }
+ // TypeScript always uses `~` when underlining, but v8 always uses `^`.
+ // We will use `^` to indicate a single point, or `~` when spanning
+ // multiple characters.
+ let ch = if length > 1 { '~' } else { '^' };
+ for _i in 0..length {
+ s.push(ch)
+ }
+ let underline = if self.is_error() {
+ colors::red(&s).to_string()
+ } else {
+ colors::cyan(&s).to_string()
+ };
+ write!(f, "\n{:indent$}{}", "", underline, indent = level)?;
+ }
+ }
-impl DiagnosticMessageChain {
- pub fn format_message(&self, level: usize) -> String {
- let mut s = String::new();
+ Ok(())
+ }
- s.push_str(&std::iter::repeat(" ").take(level * 2).collect::<String>());
- s.push_str(&self.message);
- s.push('\n');
- if let Some(next) = &self.next {
- let arr = next.clone();
- for dm in arr {
- s.push_str(&dm.format_message(level + 1));
+ fn fmt_related_information(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ if let Some(related_information) = self.related_information.as_ref() {
+ write!(f, "\n\n")?;
+ for info in related_information {
+ info.fmt_stack(f, 4)?;
}
}
- s
+ Ok(())
+ }
+
+ fn fmt_stack(&self, f: &mut fmt::Formatter, level: usize) -> fmt::Result {
+ self.fmt_category_and_code(f)?;
+ self.fmt_message(f, level)?;
+ self.fmt_source_line(f, level)?;
+ self.fmt_frame(f, level)
+ }
+
+ fn is_error(&self) -> bool {
+ self.category == DiagnosticCategory::Error
}
}
-#[derive(Clone, Debug, PartialEq)]
-pub enum DiagnosticCategory {
- Log, // 0
- Debug, // 1
- Info, // 2
- Error, // 3
- Warning, // 4
- Suggestion, // 5
+impl fmt::Display for Diagnostic {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ self.fmt_stack(f, 0)?;
+ self.fmt_related_information(f)
+ }
}
-impl<'de> Deserialize<'de> for DiagnosticCategory {
+#[derive(Clone, Debug)]
+pub struct Diagnostics(pub Vec<Diagnostic>);
+
+impl<'de> Deserialize<'de> for Diagnostics {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
- let s: i64 = Deserialize::deserialize(deserializer)?;
- Ok(DiagnosticCategory::from(s))
+ let items: Vec<Diagnostic> = Deserialize::deserialize(deserializer)?;
+ Ok(Diagnostics(items))
}
}
-impl From<i64> for DiagnosticCategory {
- fn from(value: i64) -> Self {
- match value {
- 0 => DiagnosticCategory::Log,
- 1 => DiagnosticCategory::Debug,
- 2 => DiagnosticCategory::Info,
- 3 => DiagnosticCategory::Error,
- 4 => DiagnosticCategory::Warning,
- 5 => DiagnosticCategory::Suggestion,
- _ => panic!("Unknown value: {}", value),
+impl fmt::Display for Diagnostics {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ let mut i = 0;
+ for item in &self.0 {
+ if i > 0 {
+ write!(f, "\n\n")?;
+ }
+ write!(f, "{}", item.to_string())?;
+ i += 1;
}
+
+ if i > 1 {
+ write!(f, "\n\nFound {} errors.", i)?;
+ }
+
+ Ok(())
}
}
+impl Error for Diagnostics {}
+
#[cfg(test)]
mod tests {
use super::*;
- use crate::colors::strip_ansi_codes;
-
- fn diagnostic1() -> Diagnostic {
- Diagnostic {
- items: vec![
- DiagnosticItem {
- message: "Type '(o: T) => { v: any; f: (x: B) => string; }[]' is not assignable to type '(r: B) => Value<B>[]'.".to_string(),
- message_chain: Some(DiagnosticMessageChain {
- message: "Type '(o: T) => { v: any; f: (x: B) => string; }[]' is not assignable to type '(r: B) => Value<B>[]'.".to_string(),
- code: 2322,
- category: DiagnosticCategory::Error,
- next: Some(vec![DiagnosticMessageChain {
- message: "Types of parameters 'o' and 'r' are incompatible.".to_string(),
- code: 2328,
- category: DiagnosticCategory::Error,
- next: Some(vec![DiagnosticMessageChain {
- message: "Type 'B' is not assignable to type 'T'.".to_string(),
- code: 2322,
- category: DiagnosticCategory::Error,
- next: None,
- }]),
- }]),
- }),
- code: 2322,
- category: DiagnosticCategory::Error,
- start_position: Some(267),
- end_position: Some(273),
- source_line: Some(" values: o => [".to_string()),
- line_number: Some(18),
- script_resource_name: Some("deno/tests/complex_diagnostics.ts".to_string()),
- start_column: Some(2),
- end_column: Some(8),
- related_information: Some(vec![
- DiagnosticItem {
- message: "The expected type comes from property 'values' which is declared here on type 'SettingsInterface<B>'".to_string(),
- message_chain: None,
- related_information: None,
- code: 6500,
- source_line: Some(" values?: (r: T) => Array<Value<T>>;".to_string()),
- script_resource_name: Some("deno/tests/complex_diagnostics.ts".to_string()),
- line_number: Some(6),
- start_position: Some(94),
- end_position: Some(100),
- category: DiagnosticCategory::Info,
- start_column: Some(2),
- end_column: Some(8),
- }
- ])
- }
- ]
- }
- }
+ use colors::strip_ansi_codes;
+ use serde_json::json;
- fn diagnostic2() -> Diagnostic {
- Diagnostic {
- items: vec![
- DiagnosticItem {
- message: "Example 1".to_string(),
- message_chain: None,
- code: 2322,
- category: DiagnosticCategory::Error,
- start_position: Some(267),
- end_position: Some(273),
- source_line: Some(" values: o => [".to_string()),
- line_number: Some(18),
- script_resource_name: Some(
- "deno/tests/complex_diagnostics.ts".to_string(),
- ),
- start_column: Some(2),
- end_column: Some(8),
- related_information: None,
+ #[test]
+ fn test_de_diagnostics() {
+ let value = json!([
+ {
+ "messageText": "Unknown compiler option 'invalid'.",
+ "category": 1,
+ "code": 5023
+ },
+ {
+ "start": {
+ "line": 0,
+ "character": 0
},
- DiagnosticItem {
- message: "Example 2".to_string(),
- message_chain: None,
- code: 2000,
- category: DiagnosticCategory::Error,
- start_position: Some(2),
- end_position: Some(2),
- source_line: Some(" values: undefined,".to_string()),
- line_number: Some(128),
- script_resource_name: Some("/foo/bar.ts".to_string()),
- start_column: Some(2),
- end_column: Some(8),
- related_information: None,
+ "end": {
+ "line": 0,
+ "character": 7
},
- ],
- }
- }
-
- #[test]
- fn from_json() {
- let r = serde_json::from_str::<Diagnostic>(
- &r#"{
- "items": [
+ "fileName": "test.ts",
+ "messageText": "Cannot find name 'console'. Do you need to change your target library? Try changing the `lib` compiler option to include 'dom'.",
+ "sourceLine": "console.log(\"a\");",
+ "category": 1,
+ "code": 2584
+ },
+ {
+ "start": {
+ "line": 7,
+ "character": 0
+ },
+ "end": {
+ "line": 7,
+ "character": 7
+ },
+ "fileName": "test.ts",
+ "messageText": "Cannot find name 'foo_Bar'. Did you mean 'foo_bar'?",
+ "sourceLine": "foo_Bar();",
+ "relatedInformation": [
{
- "message": "Type '{ a(): { b: number; }; }' is not assignable to type '{ a(): { b: string; }; }'.",
- "messageChain": {
- "message": "Type '{ a(): { b: number; }; }' is not assignable to type '{ a(): { b: string; }; }'.",
- "code": 2322,
- "category": 3,
+ "start": {
+ "line": 3,
+ "character": 9
+ },
+ "end": {
+ "line": 3,
+ "character": 16
+ },
+ "fileName": "test.ts",
+ "messageText": "'foo_bar' is declared here.",
+ "sourceLine": "function foo_bar() {",
+ "category": 3,
+ "code": 2728
+ }
+ ],
+ "category": 1,
+ "code": 2552
+ },
+ {
+ "start": {
+ "line": 18,
+ "character": 0
+ },
+ "end": {
+ "line": 18,
+ "character": 1
+ },
+ "fileName": "test.ts",
+ "messageChain": {
+ "messageText": "Type '{ a: { b: { c(): { d: number; }; }; }; }' is not assignable to type '{ a: { b: { c(): { d: string; }; }; }; }'.",
+ "category": 1,
+ "code": 2322,
+ "next": [
+ {
+ "messageText": "The types of 'a.b.c().d' are incompatible between these types.",
+ "category": 1,
+ "code": 2200,
"next": [
{
- "message": "Types of property 'a' are incompatible.",
- "code": 2326,
- "category": 3
+ "messageText": "Type 'number' is not assignable to type 'string'.",
+ "category": 1,
+ "code": 2322
}
]
- },
- "code": 2322,
- "category": 3,
- "startPosition": 352,
- "endPosition": 353,
- "sourceLine": "x = y;",
- "lineNumber": 29,
- "scriptResourceName": "/deno/tests/error_003_typescript.ts",
- "startColumn": 0,
- "endColumn": 1
- }
- ]
- }"#,
- ).unwrap();
- let expected =
- Diagnostic {
- items: vec![
- DiagnosticItem {
- message: "Type \'{ a(): { b: number; }; }\' is not assignable to type \'{ a(): { b: string; }; }\'.".to_string(),
- message_chain: Some(
- DiagnosticMessageChain {
- message: "Type \'{ a(): { b: number; }; }\' is not assignable to type \'{ a(): { b: string; }; }\'.".to_string(),
- code: 2322,
- category: DiagnosticCategory::Error,
- next: Some(vec![
- DiagnosticMessageChain {
- message: "Types of property \'a\' are incompatible.".to_string(),
- code: 2326,
- category: DiagnosticCategory::Error,
- next: None,
- }
- ])
- }
- ),
- related_information: None,
- source_line: Some("x = y;".to_string()),
- line_number: Some(29),
- script_resource_name: Some("/deno/tests/error_003_typescript.ts".to_string()),
- start_position: Some(352),
- end_position: Some(353),
- category: DiagnosticCategory::Error,
- code: 2322,
- start_column: Some(0),
- end_column: Some(1)
- }
- ]
- };
- assert_eq!(expected, r);
+ }
+ ]
+ },
+ "sourceLine": "x = y;",
+ "code": 2322,
+ "category": 1
+ }
+ ]);
+ let diagnostics: Diagnostics =
+ serde_json::from_value(value).expect("cannot deserialize");
+ assert_eq!(diagnostics.0.len(), 4);
+ assert!(diagnostics.0[0].source_line.is_none());
+ assert!(diagnostics.0[0].file_name.is_none());
+ assert!(diagnostics.0[0].start.is_none());
+ assert!(diagnostics.0[0].end.is_none());
+ assert!(diagnostics.0[0].message_text.is_some());
+ assert!(diagnostics.0[0].message_chain.is_none());
+ assert!(diagnostics.0[0].related_information.is_none());
+ assert!(diagnostics.0[1].source_line.is_some());
+ assert!(diagnostics.0[1].file_name.is_some());
+ assert!(diagnostics.0[1].start.is_some());
+ assert!(diagnostics.0[1].end.is_some());
+ assert!(diagnostics.0[1].message_text.is_some());
+ assert!(diagnostics.0[1].message_chain.is_none());
+ assert!(diagnostics.0[1].related_information.is_none());
+ assert!(diagnostics.0[2].source_line.is_some());
+ assert!(diagnostics.0[2].file_name.is_some());
+ assert!(diagnostics.0[2].start.is_some());
+ assert!(diagnostics.0[2].end.is_some());
+ assert!(diagnostics.0[2].message_text.is_some());
+ assert!(diagnostics.0[2].message_chain.is_none());
+ assert!(diagnostics.0[2].related_information.is_some());
}
#[test]
- fn diagnostic_to_string1() {
- let d = diagnostic1();
- let expected = "TS2322 [ERROR]: Type \'(o: T) => { v: any; f: (x: B) => string; }[]\' is not assignable to type \'(r: B) => Value<B>[]\'.\n Types of parameters \'o\' and \'r\' are incompatible.\n Type \'B\' is not assignable to type \'T\'.\n values: o => [\n ~~~~~~\n at deno/tests/complex_diagnostics.ts:19:3\n\n The expected type comes from property \'values\' which is declared here on type \'SettingsInterface<B>\'\n values?: (r: T) => Array<Value<T>>;\n ~~~~~~\n at deno/tests/complex_diagnostics.ts:7:3";
- assert_eq!(expected, strip_ansi_codes(&d.to_string()));
+ fn test_diagnostics_no_source() {
+ let value = json!([
+ {
+ "messageText": "Unknown compiler option 'invalid'.",
+ "category":1,
+ "code":5023
+ }
+ ]);
+ let diagnostics: Diagnostics = serde_json::from_value(value).unwrap();
+ let actual = format!("{}", diagnostics);
+ assert_eq!(
+ strip_ansi_codes(&actual),
+ "TS5023 [ERROR]: Unknown compiler option \'invalid\'."
+ );
}
#[test]
- fn diagnostic_to_string2() {
- let d = diagnostic2();
- let expected = "TS2322 [ERROR]: Example 1\n values: o => [\n ~~~~~~\n at deno/tests/complex_diagnostics.ts:19:3\n\nTS2000 [ERROR]: Example 2\n values: undefined,\n ~~~~~~\n at /foo/bar.ts:129:3\n\nFound 2 errors.";
- assert_eq!(expected, strip_ansi_codes(&d.to_string()));
+ fn test_diagnostics_basic() {
+ let value = json!([
+ {
+ "start": {
+ "line": 0,
+ "character": 0
+ },
+ "end": {
+ "line": 0,
+ "character": 7
+ },
+ "fileName": "test.ts",
+ "messageText": "Cannot find name 'console'. Do you need to change your target library? Try changing the `lib` compiler option to include 'dom'.",
+ "sourceLine": "console.log(\"a\");",
+ "category": 1,
+ "code": 2584
+ }
+ ]);
+ let diagnostics: Diagnostics = serde_json::from_value(value).unwrap();
+ let actual = format!("{}", diagnostics);
+ assert_eq!(strip_ansi_codes(&actual), "TS2584 [ERROR]: Cannot find name \'console\'. Do you need to change your target library? Try changing the `lib` compiler option to include \'dom\'.\nconsole.log(\"a\");\n~~~~~~~\n at test.ts:1:1");
}
#[test]
- fn test_format_none_frame() {
- let actual = format_maybe_frame(None, None, None);
- assert_eq!(actual, "");
+ fn test_diagnostics_related_info() {
+ let value = json!([
+ {
+ "start": {
+ "line": 7,
+ "character": 0
+ },
+ "end": {
+ "line": 7,
+ "character": 7
+ },
+ "fileName": "test.ts",
+ "messageText": "Cannot find name 'foo_Bar'. Did you mean 'foo_bar'?",
+ "sourceLine": "foo_Bar();",
+ "relatedInformation": [
+ {
+ "start": {
+ "line": 3,
+ "character": 9
+ },
+ "end": {
+ "line": 3,
+ "character": 16
+ },
+ "fileName": "test.ts",
+ "messageText": "'foo_bar' is declared here.",
+ "sourceLine": "function foo_bar() {",
+ "category": 3,
+ "code": 2728
+ }
+ ],
+ "category": 1,
+ "code": 2552
+ }
+ ]);
+ let diagnostics: Diagnostics = serde_json::from_value(value).unwrap();
+ let actual = format!("{}", diagnostics);
+ assert_eq!(strip_ansi_codes(&actual), "TS2552 [ERROR]: Cannot find name \'foo_Bar\'. Did you mean \'foo_bar\'?\nfoo_Bar();\n~~~~~~~\n at test.ts:8:1\n\n \'foo_bar\' is declared here.\n function foo_bar() {\n ~~~~~~~\n at test.ts:4:10");
}
#[test]
- fn test_format_some_frame() {
- let actual =
- format_maybe_frame(Some("file://foo/bar.ts"), Some(1), Some(2));
- assert_eq!(strip_ansi_codes(&actual), "file://foo/bar.ts:1:2");
+ fn test_unstable_suggestion() {
+ let value = json![
+ {
+ "start": {
+ "line": 0,
+ "character": 17
+ },
+ "end": {
+ "line": 0,
+ "character": 21
+ },
+ "fileName": "file:///cli/tests/unstable_ts2551.ts",
+ "messageText": "Property 'ppid' does not exist on type 'typeof Deno'. Did you mean 'pid'?",
+ "sourceLine": "console.log(Deno.ppid);",
+ "relatedInformation": [
+ {
+ "start": {
+ "line": 89,
+ "character": 15
+ },
+ "end": {
+ "line": 89,
+ "character": 18
+ },
+ "fileName": "asset:///lib.deno.ns.d.ts",
+ "messageText": "'pid' is declared here.",
+ "sourceLine": " export const pid: number;",
+ "category": 3,
+ "code": 2728
+ }
+ ],
+ "category": 1,
+ "code": 2551
+ }
+ ];
+ let diagnostics: Diagnostic = serde_json::from_value(value).unwrap();
+ let actual = format!("{}", diagnostics);
+ assert_eq!(strip_ansi_codes(&actual), "TS2551 [ERROR]: Property \'ppid\' does not exist on type \'typeof Deno\'. \'Deno.ppid\' is an unstable API. Did you forget to run with the \'--unstable\' flag, or did you mean \'pid\'?\nconsole.log(Deno.ppid);\n ~~~~\n at file:///cli/tests/unstable_ts2551.ts:1:18\n\n \'pid\' is declared here.\n export const pid: number;\n ~~~\n at asset:///lib.deno.ns.d.ts:90:16");
}
}
diff --git a/cli/dts/lib.deno.unstable.d.ts b/cli/dts/lib.deno.unstable.d.ts
index fad31a3f2..04c08b893 100644
--- a/cli/dts/lib.deno.unstable.d.ts
+++ b/cli/dts/lib.deno.unstable.d.ts
@@ -188,12 +188,10 @@ declare namespace Deno {
/** The log category for a diagnostic message. */
export enum DiagnosticCategory {
- Log = 0,
- Debug = 1,
- Info = 2,
- Error = 3,
- Warning = 4,
- Suggestion = 5,
+ Warning = 0,
+ Error = 1,
+ Suggestion = 2,
+ Message = 3,
}
export interface DiagnosticMessageChain {
@@ -203,37 +201,33 @@ declare namespace Deno {
next?: DiagnosticMessageChain[];
}
- export interface DiagnosticItem {
+ export interface Diagnostic {
/** A string message summarizing the diagnostic. */
- message: string;
+ messageText?: string;
/** An ordered array of further diagnostics. */
messageChain?: DiagnosticMessageChain;
/** Information related to the diagnostic. This is present when there is a
* suggestion or other additional diagnostic information */
- relatedInformation?: DiagnosticItem[];
+ relatedInformation?: Diagnostic[];
/** The text of the source line related to the diagnostic. */
sourceLine?: string;
- /** The line number that is related to the diagnostic. */
- lineNumber?: number;
- /** The name of the script resource related to the diagnostic. */
- scriptResourceName?: string;
- /** The start position related to the diagnostic. */
- startPosition?: number;
- /** The end position related to the diagnostic. */
- endPosition?: number;
+ source?: string;
+ /** The start position of the error. Zero based index. */
+ start?: {
+ line: number;
+ character: number;
+ };
+ /** The end position of the error. Zero based index. */
+ end?: {
+ line: number;
+ character: number;
+ };
+ /** The filename of the resource related to the diagnostic message. */
+ fileName?: string;
/** The category of the diagnostic. */
category: DiagnosticCategory;
/** A number identifier. */
code: number;
- /** The the start column of the sourceLine related to the diagnostic. */
- startColumn?: number;
- /** The end column of the sourceLine related to the diagnostic. */
- endColumn?: number;
- }
-
- export interface Diagnostic {
- /** An array of diagnostic items. */
- items: DiagnosticItem[];
}
/** **UNSTABLE**: new API, yet to be vetted.
@@ -247,9 +241,9 @@ declare namespace Deno {
* console.log(Deno.formatDiagnostics(diagnostics)); // User friendly output of diagnostics
* ```
*
- * @param items An array of diagnostic items to format
+ * @param diagnostics An array of diagnostic items to format
*/
- export function formatDiagnostics(items: DiagnosticItem[]): string;
+ export function formatDiagnostics(diagnostics: Diagnostic[]): string;
/** **UNSTABLE**: new API, yet to be vetted.
*
@@ -530,7 +524,7 @@ declare namespace Deno {
rootName: string,
sources?: Record<string, string>,
options?: CompilerOptions,
- ): Promise<[DiagnosticItem[] | undefined, Record<string, string>]>;
+ ): Promise<[Diagnostic[] | undefined, Record<string, string>]>;
/** **UNSTABLE**: new API, yet to be vetted.
*
@@ -573,7 +567,7 @@ declare namespace Deno {
rootName: string,
sources?: Record<string, string>,
options?: CompilerOptions,
- ): Promise<[DiagnosticItem[] | undefined, string]>;
+ ): Promise<[Diagnostic[] | undefined, string]>;
/** **UNSTABLE**: Should not have same name as `window.location` type. */
interface Location {
diff --git a/cli/ops/errors.rs b/cli/ops/errors.rs
index 40ec1da30..c1b07a9a2 100644
--- a/cli/ops/errors.rs
+++ b/cli/ops/errors.rs
@@ -1,6 +1,6 @@
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
-use crate::diagnostics::Diagnostic;
+use crate::diagnostics::Diagnostics;
use crate::source_maps::get_orig_position;
use crate::source_maps::CachedMaps;
use deno_core::ErrBox;
@@ -52,6 +52,6 @@ fn op_format_diagnostic(
args: Value,
_zero_copy: &mut [ZeroCopyBuf],
) -> Result<Value, ErrBox> {
- let diagnostic = serde_json::from_value::<Diagnostic>(args)?;
+ let diagnostic: Diagnostics = serde_json::from_value(args)?;
Ok(json!(diagnostic.to_string()))
}
diff --git a/cli/rt/40_diagnostics.js b/cli/rt/40_diagnostics.js
index 110d3d767..2b7457853 100644
--- a/cli/rt/40_diagnostics.js
+++ b/cli/rt/40_diagnostics.js
@@ -6,19 +6,15 @@
((window) => {
const DiagnosticCategory = {
- 0: "Log",
- 1: "Debug",
- 2: "Info",
- 3: "Error",
- 4: "Warning",
- 5: "Suggestion",
+ 0: "Warning",
+ 1: "Error",
+ 2: "Suggestion",
+ 3: "Message",
- Log: 0,
- Debug: 1,
- Info: 2,
- Error: 3,
- Warning: 4,
- Suggestion: 5,
+ Warning: 0,
+ Error: 1,
+ Suggestion: 2,
+ Message: 3,
};
window.__bootstrap.diagnostics = {
diff --git a/cli/rt/40_error_stack.js b/cli/rt/40_error_stack.js
index 5d1a077ad..2b7c42475 100644
--- a/cli/rt/40_error_stack.js
+++ b/cli/rt/40_error_stack.js
@@ -8,8 +8,8 @@
const internals = window.__bootstrap.internals;
const dispatchJson = window.__bootstrap.dispatchJson;
- function opFormatDiagnostics(items) {
- return dispatchJson.sendSync("op_format_diagnostic", { items });
+ function opFormatDiagnostics(diagnostics) {
+ return dispatchJson.sendSync("op_format_diagnostic", diagnostics);
}
function opApplySourceMap(location) {
diff --git a/cli/tests/unit/format_error_test.ts b/cli/tests/unit/format_error_test.ts
index ae7559c82..f769cc18c 100644
--- a/cli/tests/unit/format_error_test.ts
+++ b/cli/tests/unit/format_error_test.ts
@@ -2,27 +2,33 @@
import { assert, unitTest } from "./test_util.ts";
unitTest(function formatDiagnosticBasic() {
- const fixture: Deno.DiagnosticItem[] = [
+ const fixture: Deno.Diagnostic[] = [
{
- message: "Example error",
- category: Deno.DiagnosticCategory.Error,
- sourceLine: "abcdefghijklmnopqrstuv",
- lineNumber: 1000,
- scriptResourceName: "foo.ts",
- startColumn: 1,
- endColumn: 2,
- code: 4000,
+ start: {
+ line: 0,
+ character: 0,
+ },
+ end: {
+ line: 0,
+ character: 7,
+ },
+ fileName: "test.ts",
+ messageText:
+ "Cannot find name 'console'. Do you need to change your target library? Try changing the `lib` compiler option to include 'dom'.",
+ sourceLine: `console.log("a");`,
+ category: 1,
+ code: 2584,
},
];
const out = Deno.formatDiagnostics(fixture);
- assert(out.includes("Example error"));
- assert(out.includes("foo.ts"));
+ assert(out.includes("Cannot find name"));
+ assert(out.includes("test.ts"));
});
unitTest(function formatDiagnosticError() {
let thrown = false;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
- const bad = ([{ hello: 123 }] as any) as Deno.DiagnosticItem[];
+ const bad = ([{ hello: 123 }] as any) as Deno.Diagnostic[];
try {
Deno.formatDiagnostics(bad);
} catch (e) {
diff --git a/cli/tsc.rs b/cli/tsc.rs
index fa1c79589..98e73ae21 100644
--- a/cli/tsc.rs
+++ b/cli/tsc.rs
@@ -1,8 +1,7 @@
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
use crate::colors;
-use crate::diagnostics::Diagnostic;
-use crate::diagnostics::DiagnosticItem;
+use crate::diagnostics::Diagnostics;
use crate::disk_cache::DiskCache;
use crate::file_fetcher::SourceFile;
use crate::file_fetcher::SourceFileFetcher;
@@ -396,7 +395,7 @@ struct EmittedSource {
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
struct BundleResponse {
- diagnostics: Diagnostic,
+ diagnostics: Diagnostics,
bundle_output: Option<String>,
stats: Option<Vec<Stat>>,
}
@@ -404,7 +403,7 @@ struct BundleResponse {
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
struct CompileResponse {
- diagnostics: Diagnostic,
+ diagnostics: Diagnostics,
emit_map: HashMap<String, EmittedSource>,
build_info: Option<String>,
stats: Option<Vec<Stat>>,
@@ -425,14 +424,14 @@ struct TranspileTsOptions {
#[serde(rename_all = "camelCase")]
#[allow(unused)]
struct RuntimeBundleResponse {
- diagnostics: Vec<DiagnosticItem>,
+ diagnostics: Diagnostics,
output: String,
}
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
struct RuntimeCompileResponse {
- diagnostics: Vec<DiagnosticItem>,
+ diagnostics: Diagnostics,
emit_map: HashMap<String, EmittedSource>,
}
@@ -647,7 +646,7 @@ impl TsCompiler {
let compile_response: CompileResponse = serde_json::from_str(&json_str)?;
- if !compile_response.diagnostics.items.is_empty() {
+ if !compile_response.diagnostics.0.is_empty() {
return Err(ErrBox::error(compile_response.diagnostics.to_string()));
}
@@ -769,7 +768,7 @@ impl TsCompiler {
maybe_log_stats(bundle_response.stats);
- if !bundle_response.diagnostics.items.is_empty() {
+ if !bundle_response.diagnostics.0.is_empty() {
return Err(ErrBox::error(bundle_response.diagnostics.to_string()));
}
@@ -1287,7 +1286,7 @@ pub async fn runtime_compile(
let response: RuntimeCompileResponse = serde_json::from_str(&json_str)?;
- if response.diagnostics.is_empty() && sources.is_none() {
+ if response.diagnostics.0.is_empty() && sources.is_none() {
compiler.cache_emitted_files(response.emit_map)?;
}
diff --git a/cli/tsc/99_main_compiler.js b/cli/tsc/99_main_compiler.js
index 895c6e846..41b52d283 100644
--- a/cli/tsc/99_main_compiler.js
+++ b/cli/tsc/99_main_compiler.js
@@ -24,262 +24,62 @@ delete Object.prototype.__proto__;
const errorStack = window.__bootstrap.errorStack;
const errors = window.__bootstrap.errors.errors;
- function opNow() {
- const res = dispatchJson.sendSync("op_now");
- return res.seconds * 1e3 + res.subsecNanos / 1e6;
- }
-
- const DiagnosticCategory = {
- 0: "Log",
- 1: "Debug",
- 2: "Info",
- 3: "Error",
- 4: "Warning",
- 5: "Suggestion",
-
- Log: 0,
- Debug: 1,
- Info: 2,
- Error: 3,
- Warning: 4,
- Suggestion: 5,
- };
-
- const unstableDenoGlobalProperties = [
- "CompilerOptions",
- "DatagramConn",
- "Diagnostic",
- "DiagnosticCategory",
- "DiagnosticItem",
- "DiagnosticMessageChain",
- "EnvPermissionDescriptor",
- "HrtimePermissionDescriptor",
- "HttpClient",
- "LinuxSignal",
- "Location",
- "MacOSSignal",
- "NetPermissionDescriptor",
- "PermissionDescriptor",
- "PermissionName",
- "PermissionState",
- "PermissionStatus",
- "Permissions",
- "PluginPermissionDescriptor",
- "ReadPermissionDescriptor",
- "RunPermissionDescriptor",
- "ShutdownMode",
- "Signal",
- "SignalStream",
- "StartTlsOptions",
- "SymlinkOptions",
- "TranspileOnlyResult",
- "UnixConnectOptions",
- "UnixListenOptions",
- "WritePermissionDescriptor",
- "applySourceMap",
- "bundle",
- "compile",
- "connect",
- "consoleSize",
- "createHttpClient",
- "fdatasync",
- "fdatasyncSync",
- "formatDiagnostics",
- "futime",
- "futimeSync",
- "fstat",
- "fstatSync",
- "fsync",
- "fsyncSync",
- "ftruncate",
- "ftruncateSync",
- "hostname",
- "kill",
- "link",
- "linkSync",
- "listen",
- "listenDatagram",
- "loadavg",
- "mainModule",
- "openPlugin",
- "osRelease",
- "permissions",
- "ppid",
- "setRaw",
- "shutdown",
- "signal",
- "signals",
- "startTls",
- "symlink",
- "symlinkSync",
- "transpileOnly",
- "umask",
- "utime",
- "utimeSync",
- ];
-
- function transformMessageText(messageText, code) {
- switch (code) {
- case 2339: {
- const property = messageText
- .replace(/^Property '/, "")
- .replace(/' does not exist on type 'typeof Deno'\./, "");
-
- if (
- messageText.endsWith("on type 'typeof Deno'.") &&
- unstableDenoGlobalProperties.includes(property)
- ) {
- return `${messageText} 'Deno.${property}' is an unstable API. Did you forget to run with the '--unstable' flag?`;
- }
- break;
- }
- case 2551: {
- const suggestionMessagePattern = / Did you mean '(.+)'\?$/;
- const property = messageText
- .replace(/^Property '/, "")
- .replace(/' does not exist on type 'typeof Deno'\./, "")
- .replace(suggestionMessagePattern, "");
- const suggestion = messageText.match(suggestionMessagePattern);
- const replacedMessageText = messageText.replace(
- suggestionMessagePattern,
- "",
- );
- if (suggestion && unstableDenoGlobalProperties.includes(property)) {
- const suggestedProperty = suggestion[1];
- return `${replacedMessageText} 'Deno.${property}' is an unstable API. Did you forget to run with the '--unstable' flag, or did you mean '${suggestedProperty}'?`;
- }
- break;
- }
- }
-
- return messageText;
- }
-
- function fromDiagnosticCategory(category) {
- switch (category) {
- case ts.DiagnosticCategory.Error:
- return DiagnosticCategory.Error;
- case ts.DiagnosticCategory.Message:
- return DiagnosticCategory.Info;
- case ts.DiagnosticCategory.Suggestion:
- return DiagnosticCategory.Suggestion;
- case ts.DiagnosticCategory.Warning:
- return DiagnosticCategory.Warning;
- default:
- throw new Error(
- `Unexpected DiagnosticCategory: "${category}"/"${
- ts.DiagnosticCategory[category]
- }"`,
- );
- }
- }
-
- function getSourceInformation(sourceFile, start, length) {
- const scriptResourceName = sourceFile.fileName;
- const {
- line: lineNumber,
- character: startColumn,
- } = sourceFile.getLineAndCharacterOfPosition(start);
- const endPosition = sourceFile.getLineAndCharacterOfPosition(
- start + length,
- );
- const endColumn = lineNumber === endPosition.line
- ? endPosition.character
- : startColumn;
- const lastLineInFile = sourceFile.getLineAndCharacterOfPosition(
- sourceFile.text.length,
- ).line;
- const lineStart = sourceFile.getPositionOfLineAndCharacter(lineNumber, 0);
- const lineEnd = lineNumber < lastLineInFile
- ? sourceFile.getPositionOfLineAndCharacter(lineNumber + 1, 0)
- : sourceFile.text.length;
- const sourceLine = sourceFile.text
- .slice(lineStart, lineEnd)
- .replace(/\s+$/g, "")
- .replace("\t", " ");
- return {
- sourceLine,
- lineNumber,
- scriptResourceName,
- startColumn,
- endColumn,
- };
- }
-
- function fromDiagnosticMessageChain(messageChain) {
- if (!messageChain) {
- return undefined;
+ /**
+ * @param {import("../dts/typescript").DiagnosticRelatedInformation} diagnostic
+ */
+ function fromRelatedInformation({
+ start,
+ length,
+ file,
+ messageText: msgText,
+ ...ri
+ }) {
+ let messageText;
+ let messageChain;
+ if (typeof msgText === "object") {
+ messageChain = msgText;
+ } else {
+ messageText = msgText;
}
-
- return messageChain.map(({ messageText, code, category, next }) => {
- const message = transformMessageText(messageText, code);
+ if (start !== undefined && length !== undefined && file) {
+ const startPos = file.getLineAndCharacterOfPosition(start);
+ const sourceLine = file.getFullText().split("\n")[startPos.line];
+ const fileName = file.fileName;
return {
- message,
- code,
- category: fromDiagnosticCategory(category),
- next: fromDiagnosticMessageChain(next),
+ start: startPos,
+ end: file.getLineAndCharacterOfPosition(start + length),
+ fileName,
+ messageChain,
+ messageText,
+ sourceLine,
+ ...ri,
};
- });
- }
-
- function parseDiagnostic(item) {
- const {
- messageText,
- category: sourceCategory,
- code,
- file,
- start: startPosition,
- length,
- } = item;
- const sourceInfo = file && startPosition && length
- ? getSourceInformation(file, startPosition, length)
- : undefined;
- const endPosition = startPosition && length
- ? startPosition + length
- : undefined;
- const category = fromDiagnosticCategory(sourceCategory);
-
- let message;
- let messageChain;
- if (typeof messageText === "string") {
- message = transformMessageText(messageText, code);
} else {
- message = transformMessageText(messageText.messageText, messageText.code);
- messageChain = fromDiagnosticMessageChain([messageText])[0];
+ return {
+ messageChain,
+ messageText,
+ ...ri,
+ };
}
-
- const base = {
- message,
- messageChain,
- code,
- category,
- startPosition,
- endPosition,
- };
-
- return sourceInfo ? { ...base, ...sourceInfo } : base;
}
- function parseRelatedInformation(relatedInformation) {
- const result = [];
- for (const item of relatedInformation) {
- result.push(parseDiagnostic(item));
- }
- return result;
+ /**
+ * @param {import("../dts/typescript").Diagnostic[]} diagnostics
+ */
+ function fromTypeScriptDiagnostic(diagnostics) {
+ return diagnostics.map(({ relatedInformation: ri, source, ...diag }) => {
+ const value = fromRelatedInformation(diag);
+ value.relatedInformation = ri
+ ? ri.map(fromRelatedInformation)
+ : undefined;
+ value.source = source;
+ return value;
+ });
}
- function fromTypeScriptDiagnostic(diagnostics) {
- const items = [];
- for (const sourceDiagnostic of diagnostics) {
- const item = parseDiagnostic(sourceDiagnostic);
- if (sourceDiagnostic.relatedInformation) {
- item.relatedInformation = parseRelatedInformation(
- sourceDiagnostic.relatedInformation,
- );
- }
- items.push(item);
- }
- return { items };
+ function opNow() {
+ const res = dispatchJson.sendSync("op_now");
+ return res.seconds * 1e3 + res.subsecNanos / 1e6;
}
// We really don't want to depend on JSON dispatch during snapshotting, so
@@ -1353,7 +1153,7 @@ delete Object.prototype.__proto__;
});
const maybeDiagnostics = diagnostics.length
- ? fromTypeScriptDiagnostic(diagnostics).items
+ ? fromTypeScriptDiagnostic(diagnostics)
: [];
return {
@@ -1413,7 +1213,7 @@ delete Object.prototype.__proto__;
});
const maybeDiagnostics = diagnostics.length
- ? fromTypeScriptDiagnostic(diagnostics).items
+ ? fromTypeScriptDiagnostic(diagnostics)
: [];
return {