2020-01-02 15:13:47 -05:00
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
2020-02-24 14:48:14 -05:00
2019-09-15 14:48:25 -04:00
use crate ::colors ;
2020-09-12 05:53:57 -04:00
use regex ::Regex ;
2020-05-05 12:23:15 -04:00
use serde ::Deserialize ;
use serde ::Deserializer ;
2019-07-10 18:53:48 -04:00
use std ::error ::Error ;
2019-06-04 09:03:56 -04:00
use std ::fmt ;
2020-09-12 05:53:57 -04:00
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 " ,
" formatDiagnostics " ,
" futime " ,
" futimeSync " ,
" fstat " ,
" fstatSync " ,
" ftruncate " ,
" ftruncateSync " ,
" hostname " ,
" kill " ,
" link " ,
" linkSync " ,
" listen " ,
" listenDatagram " ,
" loadavg " ,
" mainModule " ,
" openPlugin " ,
" osRelease " ,
" permissions " ,
" ppid " ,
" setRaw " ,
" shutdown " ,
" signal " ,
" signals " ,
" startTls " ,
" symlink " ,
" symlinkSync " ,
2020-09-12 10:12:14 -04:00
" systemMemoryInfo " ,
2020-10-26 10:54:27 -04:00
" systemCpuInfo " ,
2020-09-12 05:53:57 -04:00
" 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 ( ) ;
2019-06-04 09:03:56 -04:00
}
2020-09-12 05:53:57 -04:00
/// 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 ( ) ) ;
}
}
2019-06-04 09:03:56 -04:00
}
2020-09-12 05:53:57 -04:00
msg . to_string ( )
2019-06-04 09:03:56 -04:00
}
2020-09-12 05:53:57 -04:00
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 ( ) ) ;
}
}
}
2019-06-04 09:03:56 -04:00
2020-09-12 05:53:57 -04:00
msg . to_string ( )
}
_ = > msg . to_string ( ) ,
2019-06-04 09:03:56 -04:00
}
}
2020-10-13 19:52:49 -04:00
#[ derive(Clone, Debug, Eq, PartialEq) ]
2020-09-12 05:53:57 -04:00
pub enum DiagnosticCategory {
Warning ,
Error ,
Suggestion ,
Message ,
2019-07-10 18:53:48 -04:00
}
2020-09-12 05:53:57 -04:00
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 = > " " ,
}
)
}
}
2019-06-04 09:03:56 -04:00
2020-09-12 05:53:57 -04:00
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 ) )
}
}
2019-06-04 09:03:56 -04:00
2020-09-12 05:53:57 -04:00
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 ) ,
}
}
2019-06-04 09:03:56 -04:00
}
2020-10-13 19:52:49 -04:00
#[ derive(Debug, Deserialize, Clone, Eq, PartialEq) ]
2020-09-12 05:53:57 -04:00
#[ serde(rename_all = " camelCase " ) ]
pub struct DiagnosticMessageChain {
message_text : String ,
category : DiagnosticCategory ,
2020-04-20 15:39:02 -04:00
code : i64 ,
2020-09-12 05:53:57 -04:00
next : Option < Vec < DiagnosticMessageChain > > ,
2020-04-20 15:39:02 -04:00
}
2020-09-12 05:53:57 -04:00
impl DiagnosticMessageChain {
pub fn format_message ( & self , level : usize ) -> String {
let mut s = String ::new ( ) ;
2019-06-04 09:03:56 -04:00
2020-09-12 05:53:57 -04:00
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 ) ) ;
}
}
2019-06-04 09:03:56 -04:00
2020-05-16 09:41:32 -04:00
s
}
2020-04-20 15:39:02 -04:00
}
2019-06-04 09:03:56 -04:00
2020-10-13 19:52:49 -04:00
#[ derive(Debug, Deserialize, Clone, Eq, PartialEq) ]
2020-09-12 05:53:57 -04:00
#[ serde(rename_all = " camelCase " ) ]
pub struct Position {
pub line : u64 ,
pub character : u64 ,
}
2019-06-04 09:03:56 -04:00
2020-10-13 19:52:49 -04:00
#[ derive(Debug, Deserialize, Clone, Eq, PartialEq) ]
2020-09-12 05:53:57 -04:00
#[ serde(rename_all = " camelCase " ) ]
pub struct Diagnostic {
2020-10-13 19:52:49 -04:00
pub category : DiagnosticCategory ,
pub code : u64 ,
pub start : Option < Position > ,
pub end : Option < Position > ,
pub message_text : Option < String > ,
pub message_chain : Option < DiagnosticMessageChain > ,
pub source : Option < String > ,
pub source_line : Option < String > ,
pub file_name : Option < String > ,
pub related_information : Option < Vec < Diagnostic > > ,
2020-04-20 15:39:02 -04:00
}
2019-06-04 09:03:56 -04:00
2020-09-12 05:53:57 -04:00
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 ( ( ) )
}
2019-06-04 09:03:56 -04:00
}
2020-09-12 05:53:57 -04:00
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 ( ( ) )
2020-05-16 09:41:32 -04:00
}
2019-06-04 09:03:56 -04:00
}
2020-09-12 05:53:57 -04:00
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 ,
2020-04-20 15:39:02 -04:00
)
2020-09-12 05:53:57 -04:00
}
2019-06-04 09:03:56 -04:00
}
2020-09-12 05:53:57 -04:00
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 ) ? ;
}
}
2019-06-04 09:03:56 -04:00
2020-09-12 05:53:57 -04:00
Ok ( ( ) )
}
2019-09-17 12:24:44 -04:00
2020-09-12 05:53:57 -04:00
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 ) ? ;
2019-09-17 12:24:44 -04:00
}
}
2020-09-12 05:53:57 -04:00
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
2019-06-04 09:03:56 -04:00
}
}
2020-09-12 05:53:57 -04:00
impl fmt ::Display for Diagnostic {
fn fmt ( & self , f : & mut fmt ::Formatter ) -> fmt ::Result {
self . fmt_stack ( f , 0 ) ? ;
self . fmt_related_information ( f )
}
2019-06-04 09:03:56 -04:00
}
2020-10-27 20:52:20 -04:00
#[ derive(Clone, Debug, Default, Eq, PartialEq) ]
pub struct Diagnostics ( Vec < Diagnostic > ) ;
impl Diagnostics {
#[ cfg(test) ]
pub fn new ( diagnostics : Vec < Diagnostic > ) -> Self {
Diagnostics ( diagnostics )
}
pub fn is_empty ( & self ) -> bool {
self . 0. is_empty ( )
}
}
2020-09-12 05:53:57 -04:00
impl < ' de > Deserialize < ' de > for Diagnostics {
2020-05-05 12:23:15 -04:00
fn deserialize < D > ( deserializer : D ) -> Result < Self , D ::Error >
where
D : Deserializer < ' de > ,
{
2020-09-12 05:53:57 -04:00
let items : Vec < Diagnostic > = Deserialize ::deserialize ( deserializer ) ? ;
Ok ( Diagnostics ( items ) )
2020-05-05 12:23:15 -04:00
}
}
2020-09-12 05:53:57 -04:00
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 ;
2019-06-04 09:03:56 -04:00
}
2020-09-12 05:53:57 -04:00
if i > 1 {
write! ( f , " \n \n Found {} errors. " , i ) ? ;
}
Ok ( ( ) )
2019-06-04 09:03:56 -04:00
}
}
2020-09-12 05:53:57 -04:00
impl Error for Diagnostics { }
2019-06-04 09:03:56 -04:00
#[ cfg(test) ]
mod tests {
use super ::* ;
2020-09-12 05:53:57 -04:00
use colors ::strip_ansi_codes ;
2020-09-21 12:36:37 -04:00
use deno_core ::serde_json ;
use deno_core ::serde_json ::json ;
2019-06-04 09:03:56 -04:00
2020-09-12 05:53:57 -04:00
#[ test ]
fn test_de_diagnostics ( ) {
let value = json! ( [
{
" messageText " : " Unknown compiler option 'invalid'. " ,
" category " : 1 ,
" code " : 5023
} ,
{
" start " : {
" line " : 0 ,
" character " : 0
2019-06-04 09:03:56 -04:00
} ,
2020-09-12 05:53:57 -04:00
" end " : {
" line " : 0 ,
" character " : 7
2019-06-04 09:03:56 -04:00
} ,
2020-09-12 05:53:57 -04:00
" 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 " : [
2019-06-04 09:03:56 -04:00
{
2020-09-12 05:53:57 -04:00
" 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 ,
2019-09-17 12:24:44 -04:00
" next " : [
{
2020-09-12 05:53:57 -04:00
" messageText " : " Type 'number' is not assignable to type 'string'. " ,
" category " : 1 ,
" code " : 2322
2019-06-04 09:03:56 -04:00
}
2019-09-17 12:24:44 -04:00
]
2020-09-12 05:53:57 -04:00
}
]
} ,
" 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 ( ) ) ;
2019-06-04 09:03:56 -04:00
}
#[ test ]
2020-09-12 05:53:57 -04:00
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 \' . "
) ;
2019-06-04 09:03:56 -04:00
}
#[ test ]
2020-09-12 05:53:57 -04:00
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 \' . \n console.log( \" a \" ); \n ~~~~~~~ \n at test.ts:1:1 " ) ;
2019-06-04 09:03:56 -04:00
}
2020-04-20 15:39:02 -04:00
#[ test ]
2020-09-12 05:53:57 -04:00
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 \' ? \n foo_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 " ) ;
2020-04-20 15:39:02 -04:00
}
#[ test ]
2020-09-12 05:53:57 -04:00
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 \' ? \n console.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 " ) ;
2020-04-20 15:39:02 -04:00
}
2019-06-04 09:03:56 -04:00
}