mirror of
https://github.com/denoland/deno.git
synced 2025-09-28 13:14:48 +00:00
refactor: improve tsc diagnostics (#7420)
This commit is contained in:
parent
5276cc8592
commit
10fbfcbc79
8 changed files with 684 additions and 715 deletions
|
@ -1,240 +1,152 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
// 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::colors;
|
||||||
use crate::fmt_errors::format_stack;
|
|
||||||
|
use regex::Regex;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde::Deserializer;
|
use serde::Deserializer;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, PartialEq)]
|
const MAX_SOURCE_LINE_LENGTH: usize = 150;
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct Diagnostic {
|
const UNSTABLE_DENO_PROPS: &[&str] = &[
|
||||||
pub items: Vec<DiagnosticItem>,
|
"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 {
|
/// Potentially convert a "raw" diagnostic message from TSC to something that
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
/// provides a more sensible error message given a Deno runtime context.
|
||||||
let mut i = 0;
|
fn format_message(msg: &str, code: &u64) -> String {
|
||||||
for item in &self.items {
|
match code {
|
||||||
if i > 0 {
|
2339 => {
|
||||||
write!(f, "\n\n")?;
|
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)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Error for Diagnostic {
|
|
||||||
fn description(&self) -> &str {
|
|
||||||
&self.items[0].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,
|
|
||||||
|
|
||||||
/// Zero-based index to the start column on `line_number`.
|
|
||||||
pub start_column: Option<i64>,
|
|
||||||
|
|
||||||
/// Zero-based index to the end column on `line_number`.
|
|
||||||
pub end_column: Option<i64>,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn format_category_and_code(
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn format_message(
|
|
||||||
message_chain: &Option<DiagnosticMessageChain>,
|
|
||||||
message: &str,
|
|
||||||
level: usize,
|
|
||||||
) -> String {
|
|
||||||
debug!("format_message");
|
|
||||||
|
|
||||||
if let Some(message_chain) = message_chain {
|
|
||||||
let mut s = message_chain.format_message(level);
|
|
||||||
s.pop();
|
|
||||||
|
|
||||||
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());
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn format_maybe_related_information(
|
|
||||||
related_information: &Option<Vec<DiagnosticItem>>,
|
|
||||||
) -> String {
|
|
||||||
if related_information.is_none() {
|
|
||||||
return "".to_string();
|
|
||||||
}
|
|
||||||
|
|
||||||
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,
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
s
|
msg.to_string()
|
||||||
}
|
}
|
||||||
|
2551 => {
|
||||||
impl fmt::Display for DiagnosticItem {
|
if let (Some(caps_property), Some(caps_suggestion)) = (
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
MSG_MISSING_PROPERTY_DENO.captures(msg),
|
||||||
write!(
|
MSG_SUGGESTION.captures(msg),
|
||||||
f,
|
) {
|
||||||
"{}",
|
if let (Some(property), Some(suggestion)) =
|
||||||
format_stack(
|
(caps_property.get(1), caps_suggestion.get(1))
|
||||||
matches!(self.category, DiagnosticCategory::Error),
|
{
|
||||||
&format!(
|
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());
|
||||||
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
|
|
||||||
)
|
|
||||||
)?;
|
|
||||||
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>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DiagnosticMessageChain {
|
|
||||||
pub fn format_message(&self, level: usize) -> String {
|
|
||||||
let mut s = String::new();
|
|
||||||
|
|
||||||
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));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
s
|
msg.to_string()
|
||||||
|
}
|
||||||
|
_ => msg.to_string(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub enum DiagnosticCategory {
|
pub enum DiagnosticCategory {
|
||||||
Log, // 0
|
Warning,
|
||||||
Debug, // 1
|
Error,
|
||||||
Info, // 2
|
Suggestion,
|
||||||
Error, // 3
|
Message,
|
||||||
Warning, // 4
|
}
|
||||||
Suggestion, // 5
|
|
||||||
|
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 => "",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'de> Deserialize<'de> for DiagnosticCategory {
|
impl<'de> Deserialize<'de> for DiagnosticCategory {
|
||||||
|
@ -250,202 +162,464 @@ impl<'de> Deserialize<'de> for DiagnosticCategory {
|
||||||
impl From<i64> for DiagnosticCategory {
|
impl From<i64> for DiagnosticCategory {
|
||||||
fn from(value: i64) -> Self {
|
fn from(value: i64) -> Self {
|
||||||
match value {
|
match value {
|
||||||
0 => DiagnosticCategory::Log,
|
0 => DiagnosticCategory::Warning,
|
||||||
1 => DiagnosticCategory::Debug,
|
1 => DiagnosticCategory::Error,
|
||||||
2 => DiagnosticCategory::Info,
|
2 => DiagnosticCategory::Suggestion,
|
||||||
3 => DiagnosticCategory::Error,
|
3 => DiagnosticCategory::Message,
|
||||||
4 => DiagnosticCategory::Warning,
|
|
||||||
5 => DiagnosticCategory::Suggestion,
|
|
||||||
_ => panic!("Unknown value: {}", value),
|
_ => panic!("Unknown value: {}", value),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Clone)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct DiagnosticMessageChain {
|
||||||
|
message_text: String,
|
||||||
|
category: DiagnosticCategory,
|
||||||
|
code: i64,
|
||||||
|
next: Option<Vec<DiagnosticMessageChain>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DiagnosticMessageChain {
|
||||||
|
pub fn format_message(&self, level: usize) -> String {
|
||||||
|
let mut s = String::new();
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Clone)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct Position {
|
||||||
|
pub line: u64,
|
||||||
|
pub character: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
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)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Diagnostic {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
self.fmt_stack(f, 0)?;
|
||||||
|
self.fmt_related_information(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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 items: Vec<Diagnostic> = Deserialize::deserialize(deserializer)?;
|
||||||
|
Ok(Diagnostics(items))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::colors::strip_ansi_codes;
|
use colors::strip_ansi_codes;
|
||||||
|
use serde_json::json;
|
||||||
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),
|
|
||||||
}
|
|
||||||
])
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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,
|
|
||||||
},
|
|
||||||
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,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn from_json() {
|
fn test_de_diagnostics() {
|
||||||
let r = serde_json::from_str::<Diagnostic>(
|
let value = json!([
|
||||||
&r#"{
|
|
||||||
"items": [
|
|
||||||
{
|
{
|
||||||
"message": "Type '{ a(): { b: number; }; }' is not assignable to type '{ a(): { b: string; }; }'.",
|
"messageText": "Unknown compiler option 'invalid'.",
|
||||||
"messageChain": {
|
"category": 1,
|
||||||
"message": "Type '{ a(): { b: number; }; }' is not assignable to type '{ a(): { b: string; }; }'.",
|
"code": 5023
|
||||||
"code": 2322,
|
},
|
||||||
|
{
|
||||||
|
"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
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"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,
|
"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": [
|
"next": [
|
||||||
{
|
{
|
||||||
"message": "Types of property 'a' are incompatible.",
|
"messageText": "The types of 'a.b.c().d' are incompatible between these types.",
|
||||||
"code": 2326,
|
"category": 1,
|
||||||
"category": 3
|
"code": 2200,
|
||||||
|
"next": [
|
||||||
|
{
|
||||||
|
"messageText": "Type 'number' is not assignable to type 'string'.",
|
||||||
|
"category": 1,
|
||||||
|
"code": 2322
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"code": 2322,
|
|
||||||
"category": 3,
|
|
||||||
"startPosition": 352,
|
|
||||||
"endPosition": 353,
|
|
||||||
"sourceLine": "x = y;",
|
"sourceLine": "x = y;",
|
||||||
"lineNumber": 29,
|
"code": 2322,
|
||||||
"scriptResourceName": "/deno/tests/error_003_typescript.ts",
|
"category": 1
|
||||||
"startColumn": 0,
|
|
||||||
"endColumn": 1
|
|
||||||
}
|
}
|
||||||
]
|
]);
|
||||||
}"#,
|
let diagnostics: Diagnostics =
|
||||||
).unwrap();
|
serde_json::from_value(value).expect("cannot deserialize");
|
||||||
let expected =
|
assert_eq!(diagnostics.0.len(), 4);
|
||||||
Diagnostic {
|
assert!(diagnostics.0[0].source_line.is_none());
|
||||||
items: vec![
|
assert!(diagnostics.0[0].file_name.is_none());
|
||||||
DiagnosticItem {
|
assert!(diagnostics.0[0].start.is_none());
|
||||||
message: "Type \'{ a(): { b: number; }; }\' is not assignable to type \'{ a(): { b: string; }; }\'.".to_string(),
|
assert!(diagnostics.0[0].end.is_none());
|
||||||
message_chain: Some(
|
assert!(diagnostics.0[0].message_text.is_some());
|
||||||
DiagnosticMessageChain {
|
assert!(diagnostics.0[0].message_chain.is_none());
|
||||||
message: "Type \'{ a(): { b: number; }; }\' is not assignable to type \'{ a(): { b: string; }; }\'.".to_string(),
|
assert!(diagnostics.0[0].related_information.is_none());
|
||||||
code: 2322,
|
assert!(diagnostics.0[1].source_line.is_some());
|
||||||
category: DiagnosticCategory::Error,
|
assert!(diagnostics.0[1].file_name.is_some());
|
||||||
next: Some(vec![
|
assert!(diagnostics.0[1].start.is_some());
|
||||||
DiagnosticMessageChain {
|
assert!(diagnostics.0[1].end.is_some());
|
||||||
message: "Types of property \'a\' are incompatible.".to_string(),
|
assert!(diagnostics.0[1].message_text.is_some());
|
||||||
code: 2326,
|
assert!(diagnostics.0[1].message_chain.is_none());
|
||||||
category: DiagnosticCategory::Error,
|
assert!(diagnostics.0[1].related_information.is_none());
|
||||||
next: 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());
|
||||||
related_information: None,
|
assert!(diagnostics.0[2].message_chain.is_none());
|
||||||
source_line: Some("x = y;".to_string()),
|
assert!(diagnostics.0[2].related_information.is_some());
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn diagnostic_to_string1() {
|
fn test_diagnostics_no_source() {
|
||||||
let d = diagnostic1();
|
let value = json!([
|
||||||
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()));
|
"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]
|
#[test]
|
||||||
fn diagnostic_to_string2() {
|
fn test_diagnostics_basic() {
|
||||||
let d = diagnostic2();
|
let value = json!([
|
||||||
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()));
|
"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]
|
#[test]
|
||||||
fn test_format_none_frame() {
|
fn test_diagnostics_related_info() {
|
||||||
let actual = format_maybe_frame(None, None, None);
|
let value = json!([
|
||||||
assert_eq!(actual, "");
|
{
|
||||||
|
"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]
|
#[test]
|
||||||
fn test_format_some_frame() {
|
fn test_unstable_suggestion() {
|
||||||
let actual =
|
let value = json![
|
||||||
format_maybe_frame(Some("file://foo/bar.ts"), Some(1), Some(2));
|
{
|
||||||
assert_eq!(strip_ansi_codes(&actual), "file://foo/bar.ts:1:2");
|
"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");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
54
cli/dts/lib.deno.unstable.d.ts
vendored
54
cli/dts/lib.deno.unstable.d.ts
vendored
|
@ -188,12 +188,10 @@ declare namespace Deno {
|
||||||
|
|
||||||
/** The log category for a diagnostic message. */
|
/** The log category for a diagnostic message. */
|
||||||
export enum DiagnosticCategory {
|
export enum DiagnosticCategory {
|
||||||
Log = 0,
|
Warning = 0,
|
||||||
Debug = 1,
|
Error = 1,
|
||||||
Info = 2,
|
Suggestion = 2,
|
||||||
Error = 3,
|
Message = 3,
|
||||||
Warning = 4,
|
|
||||||
Suggestion = 5,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DiagnosticMessageChain {
|
export interface DiagnosticMessageChain {
|
||||||
|
@ -203,37 +201,33 @@ declare namespace Deno {
|
||||||
next?: DiagnosticMessageChain[];
|
next?: DiagnosticMessageChain[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DiagnosticItem {
|
export interface Diagnostic {
|
||||||
/** A string message summarizing the diagnostic. */
|
/** A string message summarizing the diagnostic. */
|
||||||
message: string;
|
messageText?: string;
|
||||||
/** An ordered array of further diagnostics. */
|
/** An ordered array of further diagnostics. */
|
||||||
messageChain?: DiagnosticMessageChain;
|
messageChain?: DiagnosticMessageChain;
|
||||||
/** Information related to the diagnostic. This is present when there is a
|
/** Information related to the diagnostic. This is present when there is a
|
||||||
* suggestion or other additional diagnostic information */
|
* suggestion or other additional diagnostic information */
|
||||||
relatedInformation?: DiagnosticItem[];
|
relatedInformation?: Diagnostic[];
|
||||||
/** The text of the source line related to the diagnostic. */
|
/** The text of the source line related to the diagnostic. */
|
||||||
sourceLine?: string;
|
sourceLine?: string;
|
||||||
/** The line number that is related to the diagnostic. */
|
source?: string;
|
||||||
lineNumber?: number;
|
/** The start position of the error. Zero based index. */
|
||||||
/** The name of the script resource related to the diagnostic. */
|
start?: {
|
||||||
scriptResourceName?: string;
|
line: number;
|
||||||
/** The start position related to the diagnostic. */
|
character: number;
|
||||||
startPosition?: number;
|
};
|
||||||
/** The end position related to the diagnostic. */
|
/** The end position of the error. Zero based index. */
|
||||||
endPosition?: number;
|
end?: {
|
||||||
|
line: number;
|
||||||
|
character: number;
|
||||||
|
};
|
||||||
|
/** The filename of the resource related to the diagnostic message. */
|
||||||
|
fileName?: string;
|
||||||
/** The category of the diagnostic. */
|
/** The category of the diagnostic. */
|
||||||
category: DiagnosticCategory;
|
category: DiagnosticCategory;
|
||||||
/** A number identifier. */
|
/** A number identifier. */
|
||||||
code: number;
|
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.
|
/** **UNSTABLE**: new API, yet to be vetted.
|
||||||
|
@ -247,9 +241,9 @@ declare namespace Deno {
|
||||||
* console.log(Deno.formatDiagnostics(diagnostics)); // User friendly output of diagnostics
|
* 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.
|
/** **UNSTABLE**: new API, yet to be vetted.
|
||||||
*
|
*
|
||||||
|
@ -530,7 +524,7 @@ declare namespace Deno {
|
||||||
rootName: string,
|
rootName: string,
|
||||||
sources?: Record<string, string>,
|
sources?: Record<string, string>,
|
||||||
options?: CompilerOptions,
|
options?: CompilerOptions,
|
||||||
): Promise<[DiagnosticItem[] | undefined, Record<string, string>]>;
|
): Promise<[Diagnostic[] | undefined, Record<string, string>]>;
|
||||||
|
|
||||||
/** **UNSTABLE**: new API, yet to be vetted.
|
/** **UNSTABLE**: new API, yet to be vetted.
|
||||||
*
|
*
|
||||||
|
@ -573,7 +567,7 @@ declare namespace Deno {
|
||||||
rootName: string,
|
rootName: string,
|
||||||
sources?: Record<string, string>,
|
sources?: Record<string, string>,
|
||||||
options?: CompilerOptions,
|
options?: CompilerOptions,
|
||||||
): Promise<[DiagnosticItem[] | undefined, string]>;
|
): Promise<[Diagnostic[] | undefined, string]>;
|
||||||
|
|
||||||
/** **UNSTABLE**: Should not have same name as `window.location` type. */
|
/** **UNSTABLE**: Should not have same name as `window.location` type. */
|
||||||
interface Location {
|
interface Location {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
// 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::get_orig_position;
|
||||||
use crate::source_maps::CachedMaps;
|
use crate::source_maps::CachedMaps;
|
||||||
use deno_core::ErrBox;
|
use deno_core::ErrBox;
|
||||||
|
@ -52,6 +52,6 @@ fn op_format_diagnostic(
|
||||||
args: Value,
|
args: Value,
|
||||||
_zero_copy: &mut [ZeroCopyBuf],
|
_zero_copy: &mut [ZeroCopyBuf],
|
||||||
) -> Result<Value, ErrBox> {
|
) -> Result<Value, ErrBox> {
|
||||||
let diagnostic = serde_json::from_value::<Diagnostic>(args)?;
|
let diagnostic: Diagnostics = serde_json::from_value(args)?;
|
||||||
Ok(json!(diagnostic.to_string()))
|
Ok(json!(diagnostic.to_string()))
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,19 +6,15 @@
|
||||||
|
|
||||||
((window) => {
|
((window) => {
|
||||||
const DiagnosticCategory = {
|
const DiagnosticCategory = {
|
||||||
0: "Log",
|
0: "Warning",
|
||||||
1: "Debug",
|
1: "Error",
|
||||||
2: "Info",
|
2: "Suggestion",
|
||||||
3: "Error",
|
3: "Message",
|
||||||
4: "Warning",
|
|
||||||
5: "Suggestion",
|
|
||||||
|
|
||||||
Log: 0,
|
Warning: 0,
|
||||||
Debug: 1,
|
Error: 1,
|
||||||
Info: 2,
|
Suggestion: 2,
|
||||||
Error: 3,
|
Message: 3,
|
||||||
Warning: 4,
|
|
||||||
Suggestion: 5,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
window.__bootstrap.diagnostics = {
|
window.__bootstrap.diagnostics = {
|
||||||
|
|
|
@ -8,8 +8,8 @@
|
||||||
const internals = window.__bootstrap.internals;
|
const internals = window.__bootstrap.internals;
|
||||||
const dispatchJson = window.__bootstrap.dispatchJson;
|
const dispatchJson = window.__bootstrap.dispatchJson;
|
||||||
|
|
||||||
function opFormatDiagnostics(items) {
|
function opFormatDiagnostics(diagnostics) {
|
||||||
return dispatchJson.sendSync("op_format_diagnostic", { items });
|
return dispatchJson.sendSync("op_format_diagnostic", diagnostics);
|
||||||
}
|
}
|
||||||
|
|
||||||
function opApplySourceMap(location) {
|
function opApplySourceMap(location) {
|
||||||
|
|
|
@ -2,27 +2,33 @@
|
||||||
import { assert, unitTest } from "./test_util.ts";
|
import { assert, unitTest } from "./test_util.ts";
|
||||||
|
|
||||||
unitTest(function formatDiagnosticBasic() {
|
unitTest(function formatDiagnosticBasic() {
|
||||||
const fixture: Deno.DiagnosticItem[] = [
|
const fixture: Deno.Diagnostic[] = [
|
||||||
{
|
{
|
||||||
message: "Example error",
|
start: {
|
||||||
category: Deno.DiagnosticCategory.Error,
|
line: 0,
|
||||||
sourceLine: "abcdefghijklmnopqrstuv",
|
character: 0,
|
||||||
lineNumber: 1000,
|
},
|
||||||
scriptResourceName: "foo.ts",
|
end: {
|
||||||
startColumn: 1,
|
line: 0,
|
||||||
endColumn: 2,
|
character: 7,
|
||||||
code: 4000,
|
},
|
||||||
|
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);
|
const out = Deno.formatDiagnostics(fixture);
|
||||||
assert(out.includes("Example error"));
|
assert(out.includes("Cannot find name"));
|
||||||
assert(out.includes("foo.ts"));
|
assert(out.includes("test.ts"));
|
||||||
});
|
});
|
||||||
|
|
||||||
unitTest(function formatDiagnosticError() {
|
unitTest(function formatDiagnosticError() {
|
||||||
let thrown = false;
|
let thrown = false;
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// 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 {
|
try {
|
||||||
Deno.formatDiagnostics(bad);
|
Deno.formatDiagnostics(bad);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
17
cli/tsc.rs
17
cli/tsc.rs
|
@ -1,8 +1,7 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||||
|
|
||||||
use crate::colors;
|
use crate::colors;
|
||||||
use crate::diagnostics::Diagnostic;
|
use crate::diagnostics::Diagnostics;
|
||||||
use crate::diagnostics::DiagnosticItem;
|
|
||||||
use crate::disk_cache::DiskCache;
|
use crate::disk_cache::DiskCache;
|
||||||
use crate::file_fetcher::SourceFile;
|
use crate::file_fetcher::SourceFile;
|
||||||
use crate::file_fetcher::SourceFileFetcher;
|
use crate::file_fetcher::SourceFileFetcher;
|
||||||
|
@ -396,7 +395,7 @@ struct EmittedSource {
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
struct BundleResponse {
|
struct BundleResponse {
|
||||||
diagnostics: Diagnostic,
|
diagnostics: Diagnostics,
|
||||||
bundle_output: Option<String>,
|
bundle_output: Option<String>,
|
||||||
stats: Option<Vec<Stat>>,
|
stats: Option<Vec<Stat>>,
|
||||||
}
|
}
|
||||||
|
@ -404,7 +403,7 @@ struct BundleResponse {
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
struct CompileResponse {
|
struct CompileResponse {
|
||||||
diagnostics: Diagnostic,
|
diagnostics: Diagnostics,
|
||||||
emit_map: HashMap<String, EmittedSource>,
|
emit_map: HashMap<String, EmittedSource>,
|
||||||
build_info: Option<String>,
|
build_info: Option<String>,
|
||||||
stats: Option<Vec<Stat>>,
|
stats: Option<Vec<Stat>>,
|
||||||
|
@ -425,14 +424,14 @@ struct TranspileTsOptions {
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
struct RuntimeBundleResponse {
|
struct RuntimeBundleResponse {
|
||||||
diagnostics: Vec<DiagnosticItem>,
|
diagnostics: Diagnostics,
|
||||||
output: String,
|
output: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
struct RuntimeCompileResponse {
|
struct RuntimeCompileResponse {
|
||||||
diagnostics: Vec<DiagnosticItem>,
|
diagnostics: Diagnostics,
|
||||||
emit_map: HashMap<String, EmittedSource>,
|
emit_map: HashMap<String, EmittedSource>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -647,7 +646,7 @@ impl TsCompiler {
|
||||||
|
|
||||||
let compile_response: CompileResponse = serde_json::from_str(&json_str)?;
|
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()));
|
return Err(ErrBox::error(compile_response.diagnostics.to_string()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -769,7 +768,7 @@ impl TsCompiler {
|
||||||
|
|
||||||
maybe_log_stats(bundle_response.stats);
|
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()));
|
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)?;
|
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)?;
|
compiler.cache_emitted_files(response.emit_map)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,262 +24,62 @@ delete Object.prototype.__proto__;
|
||||||
const errorStack = window.__bootstrap.errorStack;
|
const errorStack = window.__bootstrap.errorStack;
|
||||||
const errors = window.__bootstrap.errors.errors;
|
const errors = window.__bootstrap.errors.errors;
|
||||||
|
|
||||||
function opNow() {
|
/**
|
||||||
const res = dispatchJson.sendSync("op_now");
|
* @param {import("../dts/typescript").DiagnosticRelatedInformation} diagnostic
|
||||||
return res.seconds * 1e3 + res.subsecNanos / 1e6;
|
*/
|
||||||
|
function fromRelatedInformation({
|
||||||
|
start,
|
||||||
|
length,
|
||||||
|
file,
|
||||||
|
messageText: msgText,
|
||||||
|
...ri
|
||||||
|
}) {
|
||||||
|
let messageText;
|
||||||
|
let messageChain;
|
||||||
|
if (typeof msgText === "object") {
|
||||||
|
messageChain = msgText;
|
||||||
|
} else {
|
||||||
|
messageText = msgText;
|
||||||
}
|
}
|
||||||
|
if (start !== undefined && length !== undefined && file) {
|
||||||
const DiagnosticCategory = {
|
const startPos = file.getLineAndCharacterOfPosition(start);
|
||||||
0: "Log",
|
const sourceLine = file.getFullText().split("\n")[startPos.line];
|
||||||
1: "Debug",
|
const fileName = file.fileName;
|
||||||
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 {
|
return {
|
||||||
|
start: startPos,
|
||||||
|
end: file.getLineAndCharacterOfPosition(start + length),
|
||||||
|
fileName,
|
||||||
|
messageChain,
|
||||||
|
messageText,
|
||||||
sourceLine,
|
sourceLine,
|
||||||
lineNumber,
|
...ri,
|
||||||
scriptResourceName,
|
|
||||||
startColumn,
|
|
||||||
endColumn,
|
|
||||||
};
|
};
|
||||||
}
|
} else {
|
||||||
|
|
||||||
function fromDiagnosticMessageChain(messageChain) {
|
|
||||||
if (!messageChain) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
return messageChain.map(({ messageText, code, category, next }) => {
|
|
||||||
const message = transformMessageText(messageText, code);
|
|
||||||
return {
|
return {
|
||||||
message,
|
messageChain,
|
||||||
code,
|
messageText,
|
||||||
category: fromDiagnosticCategory(category),
|
...ri,
|
||||||
next: fromDiagnosticMessageChain(next),
|
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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 parseDiagnostic(item) {
|
function opNow() {
|
||||||
const {
|
const res = dispatchJson.sendSync("op_now");
|
||||||
messageText,
|
return res.seconds * 1e3 + res.subsecNanos / 1e6;
|
||||||
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];
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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 };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// We really don't want to depend on JSON dispatch during snapshotting, so
|
// 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
|
const maybeDiagnostics = diagnostics.length
|
||||||
? fromTypeScriptDiagnostic(diagnostics).items
|
? fromTypeScriptDiagnostic(diagnostics)
|
||||||
: [];
|
: [];
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -1413,7 +1213,7 @@ delete Object.prototype.__proto__;
|
||||||
});
|
});
|
||||||
|
|
||||||
const maybeDiagnostics = diagnostics.length
|
const maybeDiagnostics = diagnostics.length
|
||||||
? fromTypeScriptDiagnostic(diagnostics).items
|
? fromTypeScriptDiagnostic(diagnostics)
|
||||||
: [];
|
: [];
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue