2020-01-21 10:01:55 -05:00
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
2020-01-08 09:17:44 -05:00
// These utilities are used by compiler.ts to format TypeScript diagnostics
// into Deno Diagnostics.
import {
Diagnostic ,
DiagnosticCategory ,
DiagnosticMessageChain ,
2020-03-28 13:03:49 -04:00
DiagnosticItem ,
2020-01-08 09:17:44 -05:00
} from "./diagnostics.ts" ;
2020-05-29 06:45:20 -04: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 08:00:56 -04:00
"ppid" ,
2020-05-29 06:45:20 -04:00
] ;
function transformMessageText ( messageText : string , code : number ) : string {
2020-07-13 08:00:56 -04: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 ,
2020-07-14 15:24:17 -04:00
"" ,
2020-07-13 08:00:56 -04:00
) ;
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 06:45:20 -04:00
}
}
2020-07-13 08:00:56 -04:00
2020-05-29 06:45:20 -04:00
return messageText ;
}
2020-01-08 09:17:44 -05:00
interface SourceInformation {
sourceLine : string ;
lineNumber : number ;
scriptResourceName : string ;
startColumn : number ;
endColumn : number ;
}
function fromDiagnosticCategory (
2020-07-14 15:24:17 -04:00
category : ts.DiagnosticCategory ,
2020-01-08 09:17:44 -05:00
) : 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 (
2020-07-14 15:24:17 -04:00
` Unexpected DiagnosticCategory: " ${ category } "/" ${
ts . DiagnosticCategory [ category ]
} " ` ,
2020-01-08 09:17:44 -05:00
) ;
}
}
function getSourceInformation (
sourceFile : ts.SourceFile ,
start : number ,
2020-07-14 15:24:17 -04:00
length : number ,
2020-01-08 09:17:44 -05:00
) : SourceInformation {
const scriptResourceName = sourceFile . fileName ;
const {
line : lineNumber ,
2020-03-28 13:03:49 -04:00
character : startColumn ,
2020-01-08 09:17:44 -05:00
} = sourceFile . getLineAndCharacterOfPosition ( start ) ;
const endPosition = sourceFile . getLineAndCharacterOfPosition ( start + length ) ;
2020-07-14 15:24:17 -04:00
const endColumn = lineNumber === endPosition . line
? endPosition . character
: startColumn ;
2020-01-08 09:17:44 -05:00
const lastLineInFile = sourceFile . getLineAndCharacterOfPosition (
2020-07-14 15:24:17 -04:00
sourceFile . text . length ,
2020-01-08 09:17:44 -05:00
) . line ;
const lineStart = sourceFile . getPositionOfLineAndCharacter ( lineNumber , 0 ) ;
2020-07-14 15:24:17 -04:00
const lineEnd = lineNumber < lastLineInFile
? sourceFile . getPositionOfLineAndCharacter ( lineNumber + 1 , 0 )
: sourceFile . text . length ;
2020-01-08 09:17:44 -05:00
const sourceLine = sourceFile . text
. slice ( lineStart , lineEnd )
. replace ( /\s+$/g , "" )
. replace ( "\t" , " " ) ;
return {
sourceLine ,
lineNumber ,
scriptResourceName ,
startColumn ,
2020-03-28 13:03:49 -04:00
endColumn ,
2020-01-08 09:17:44 -05:00
} ;
}
function fromDiagnosticMessageChain (
2020-07-14 15:24:17 -04:00
messageChain : ts.DiagnosticMessageChain [ ] | undefined ,
2020-01-08 09:17:44 -05:00
) : DiagnosticMessageChain [ ] | undefined {
if ( ! messageChain ) {
return undefined ;
}
2020-05-29 06:45:20 -04:00
return messageChain . map ( ( { messageText , code , category , next } ) = > {
const message = transformMessageText ( messageText , code ) ;
2020-01-08 09:17:44 -05:00
return {
message ,
code ,
category : fromDiagnosticCategory ( category ) ,
2020-03-28 13:03:49 -04:00
next : fromDiagnosticMessageChain ( next ) ,
2020-01-08 09:17:44 -05:00
} ;
} ) ;
}
function parseDiagnostic (
2020-07-14 15:24:17 -04:00
item : ts.Diagnostic | ts . DiagnosticRelatedInformation ,
2020-01-08 09:17:44 -05:00
) : DiagnosticItem {
const {
messageText ,
category : sourceCategory ,
code ,
file ,
start : startPosition ,
2020-03-28 13:03:49 -04:00
length ,
2020-01-08 09:17:44 -05:00
} = item ;
2020-07-14 15:24:17 -04:00
const sourceInfo = file && startPosition && length
? getSourceInformation ( file , startPosition , length )
: undefined ;
const endPosition = startPosition && length
? startPosition + length
: undefined ;
2020-01-08 09:17:44 -05:00
const category = fromDiagnosticCategory ( sourceCategory ) ;
let message : string ;
let messageChain : DiagnosticMessageChain | undefined ;
if ( typeof messageText === "string" ) {
2020-05-29 06:45:20 -04:00
message = transformMessageText ( messageText , code ) ;
2020-01-08 09:17:44 -05:00
} else {
2020-05-29 06:45:20 -04:00
message = transformMessageText ( messageText . messageText , messageText . code ) ;
2020-01-08 09:17:44 -05:00
messageChain = fromDiagnosticMessageChain ( [ messageText ] ) ! [ 0 ] ;
}
const base = {
message ,
messageChain ,
code ,
category ,
startPosition ,
2020-03-28 13:03:49 -04:00
endPosition ,
2020-01-08 09:17:44 -05:00
} ;
return sourceInfo ? { . . . base , . . . sourceInfo } : base ;
}
function parseRelatedInformation (
2020-07-14 15:24:17 -04:00
relatedInformation : readonly ts . DiagnosticRelatedInformation [ ] ,
2020-01-08 09:17:44 -05:00
) : DiagnosticItem [ ] {
const result : DiagnosticItem [ ] = [ ] ;
for ( const item of relatedInformation ) {
result . push ( parseDiagnostic ( item ) ) ;
}
return result ;
}
export function fromTypeScriptDiagnostic (
2020-07-14 15:24:17 -04:00
diagnostics : readonly ts . Diagnostic [ ] ,
2020-01-08 09:17:44 -05:00
) : Diagnostic {
const items : DiagnosticItem [ ] = [ ] ;
for ( const sourceDiagnostic of diagnostics ) {
const item : DiagnosticItem = parseDiagnostic ( sourceDiagnostic ) ;
if ( sourceDiagnostic . relatedInformation ) {
item . relatedInformation = parseRelatedInformation (
2020-07-14 15:24:17 -04:00
sourceDiagnostic . relatedInformation ,
2020-01-08 09:17:44 -05:00
) ;
}
items . push ( item ) ;
}
return { items } ;
}