2020-01-21 09:01:55 -06:00
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
2020-01-09 01:17:44 +11:00
// These utilities are used by compiler.ts to format TypeScript diagnostics
// into Deno Diagnostics.
import {
Diagnostic ,
DiagnosticCategory ,
DiagnosticMessageChain ,
2020-03-29 04:03:49 +11:00
DiagnosticItem ,
2020-01-09 01:17:44 +11:00
} from "./diagnostics.ts" ;
2020-05-29 12:45:20 +02:00
const unstableDenoGlobalProperties = [
"umask" ,
"linkSync" ,
"link" ,
"symlinkSync" ,
"symlink" ,
"loadavg" ,
"osRelease" ,
"openPlugin" ,
"DiagnosticCategory" ,
"DiagnosticMessageChain" ,
"DiagnosticItem" ,
"Diagnostic" ,
"formatDiagnostics" ,
"CompilerOptions" ,
"TranspileOnlyResult" ,
"transpileOnly" ,
"compile" ,
"bundle" ,
"Location" ,
"applySourceMap" ,
"LinuxSignal" ,
"MacOSSignal" ,
"Signal" ,
"SignalStream" ,
"signal" ,
"signals" ,
"setRaw" ,
"utimeSync" ,
"utime" ,
"ShutdownMode" ,
"shutdown" ,
"DatagramConn" ,
"UnixListenOptions" ,
"listen" ,
"listenDatagram" ,
"UnixConnectOptions" ,
"connect" ,
"StartTlsOptions" ,
"startTls" ,
"kill" ,
"PermissionName" ,
"PermissionState" ,
"RunPermissionDescriptor" ,
"ReadPermissionDescriptor" ,
"WritePermissionDescriptor" ,
"NetPermissionDescriptor" ,
"EnvPermissionDescriptor" ,
"PluginPermissionDescriptor" ,
"HrtimePermissionDescriptor" ,
"PermissionDescriptor" ,
"Permissions" ,
"PermissionStatus" ,
"hostname" ,
2020-07-13 21:00:56 +09:00
"ppid" ,
2020-05-29 12:45:20 +02:00
] ;
function transformMessageText ( messageText : string , code : number ) : string {
2020-07-13 21:00:56 +09:00
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 ;
2020-05-29 12:45:20 +02:00
}
}
2020-07-13 21:00:56 +09:00
2020-05-29 12:45:20 +02:00
return messageText ;
}
2020-01-09 01:17:44 +11:00
interface SourceInformation {
sourceLine : string ;
lineNumber : number ;
scriptResourceName : string ;
startColumn : number ;
endColumn : number ;
}
function fromDiagnosticCategory (
category : ts.DiagnosticCategory
) : DiagnosticCategory {
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 : ts.SourceFile ,
start : number ,
length : number
) : SourceInformation {
const scriptResourceName = sourceFile . fileName ;
const {
line : lineNumber ,
2020-03-29 04:03:49 +11:00
character : startColumn ,
2020-01-09 01:17:44 +11:00
} = 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 ,
2020-03-29 04:03:49 +11:00
endColumn ,
2020-01-09 01:17:44 +11:00
} ;
}
function fromDiagnosticMessageChain (
messageChain : ts.DiagnosticMessageChain [ ] | undefined
) : DiagnosticMessageChain [ ] | undefined {
if ( ! messageChain ) {
return undefined ;
}
2020-05-29 12:45:20 +02:00
return messageChain . map ( ( { messageText , code , category , next } ) = > {
const message = transformMessageText ( messageText , code ) ;
2020-01-09 01:17:44 +11:00
return {
message ,
code ,
category : fromDiagnosticCategory ( category ) ,
2020-03-29 04:03:49 +11:00
next : fromDiagnosticMessageChain ( next ) ,
2020-01-09 01:17:44 +11:00
} ;
} ) ;
}
function parseDiagnostic (
item : ts.Diagnostic | ts . DiagnosticRelatedInformation
) : DiagnosticItem {
const {
messageText ,
category : sourceCategory ,
code ,
file ,
start : startPosition ,
2020-03-29 04:03:49 +11:00
length ,
2020-01-09 01:17:44 +11:00
} = item ;
const sourceInfo =
file && startPosition && length
? getSourceInformation ( file , startPosition , length )
: undefined ;
const endPosition =
startPosition && length ? startPosition + length : undefined ;
const category = fromDiagnosticCategory ( sourceCategory ) ;
let message : string ;
let messageChain : DiagnosticMessageChain | undefined ;
if ( typeof messageText === "string" ) {
2020-05-29 12:45:20 +02:00
message = transformMessageText ( messageText , code ) ;
2020-01-09 01:17:44 +11:00
} else {
2020-05-29 12:45:20 +02:00
message = transformMessageText ( messageText . messageText , messageText . code ) ;
2020-01-09 01:17:44 +11:00
messageChain = fromDiagnosticMessageChain ( [ messageText ] ) ! [ 0 ] ;
}
const base = {
message ,
messageChain ,
code ,
category ,
startPosition ,
2020-03-29 04:03:49 +11:00
endPosition ,
2020-01-09 01:17:44 +11:00
} ;
return sourceInfo ? { . . . base , . . . sourceInfo } : base ;
}
function parseRelatedInformation (
relatedInformation : readonly ts . DiagnosticRelatedInformation [ ]
) : DiagnosticItem [ ] {
const result : DiagnosticItem [ ] = [ ] ;
for ( const item of relatedInformation ) {
result . push ( parseDiagnostic ( item ) ) ;
}
return result ;
}
export function fromTypeScriptDiagnostic (
diagnostics : readonly ts . Diagnostic [ ]
) : Diagnostic {
const items : DiagnosticItem [ ] = [ ] ;
for ( const sourceDiagnostic of diagnostics ) {
const item : DiagnosticItem = parseDiagnostic ( sourceDiagnostic ) ;
if ( sourceDiagnostic . relatedInformation ) {
item . relatedInformation = parseRelatedInformation (
sourceDiagnostic . relatedInformation
) ;
}
items . push ( item ) ;
}
return { items } ;
}