2014-12-16 22:14:14 +01:00
/// <reference path="sys.ts" />
/// <reference path="emitter.ts" />
2015-07-29 04:26:18 +02:00
/// <reference path="core.ts" />
2014-12-16 22:14:14 +01:00
2015-06-12 18:01:48 +02:00
namespace ts {
2017-03-23 00:23:21 +01:00
const ignoreDiagnosticCommentRegEx = /(^\s*$)|(^\s*\/\/\/?\s*(@ts-ignore)?)/ ;
2015-10-01 01:10:52 +02:00
2018-01-05 01:56:29 +01:00
export function findConfigFile ( searchPath : string , fileExists : ( fileName : string ) = > boolean , configName = "tsconfig.json" ) : string | undefined {
2017-11-03 23:06:22 +01:00
return forEachAncestorDirectory ( searchPath , ancestor = > {
const fileName = combinePaths ( ancestor , configName ) ;
return fileExists ( fileName ) ? fileName : undefined ;
} ) ;
2015-03-24 22:03:21 +01:00
}
2015-10-01 01:10:52 +02:00
2015-07-14 02:44:50 +02:00
export function resolveTripleslashReference ( module Name : string , containingFile : string ) : string {
2015-11-04 23:02:33 +01:00
const basePath = getDirectoryPath ( containingFile ) ;
const referencedFileName = isRootedDiskPath ( module Name ) ? module Name : combinePaths ( basePath , module Name ) ;
2015-07-14 02:44:50 +02:00
return normalizePath ( referencedFileName ) ;
}
2015-10-01 01:10:52 +02:00
2016-04-06 01:33:11 +02:00
/* @internal */
2017-11-14 23:26:49 +01:00
export function computeCommonSourceDirectoryOfFilenames ( fileNames : string [ ] , currentDirectory : string , getCanonicalFileName : GetCanonicalFileName ) : string {
2016-04-01 21:41:01 +02:00
let commonPathComponents : string [ ] ;
const failed = forEach ( fileNames , sourceFile = > {
// Each file contributes into common source file path
const sourcePathComponents = getNormalizedPathComponents ( sourceFile , currentDirectory ) ;
sourcePathComponents . pop ( ) ; // The base file name is not part of the common directory path
if ( ! commonPathComponents ) {
// first file
commonPathComponents = sourcePathComponents ;
return ;
}
2016-12-18 07:44:54 +01:00
const n = Math . min ( commonPathComponents . length , sourcePathComponents . length ) ;
for ( let i = 0 ; i < n ; i ++ ) {
2016-04-01 21:41:01 +02:00
if ( getCanonicalFileName ( commonPathComponents [ i ] ) !== getCanonicalFileName ( sourcePathComponents [ i ] ) ) {
if ( i === 0 ) {
// Failed to find any common path component
return true ;
}
// New common path found that is 0 -> i-1
commonPathComponents . length = i ;
break ;
}
}
// If the sourcePathComponents was shorter than the commonPathComponents, truncate to the sourcePathComponents
if ( sourcePathComponents . length < commonPathComponents . length ) {
commonPathComponents . length = sourcePathComponents . length ;
}
} ) ;
// A common path can not be found when paths span multiple drives on windows, for example
if ( failed ) {
return "" ;
}
if ( ! commonPathComponents ) { // Can happen when all input files are .d.ts files
return currentDirectory ;
}
return getNormalizedPathFromPathComponents ( commonPathComponents ) ;
}
2016-02-09 15:23:43 +01:00
interface OutputFingerprint {
hash : string ;
byteOrderMark : boolean ;
mtime : Date ;
}
2015-03-18 22:11:50 +01:00
export function createCompilerHost ( options : CompilerOptions , setParentNodes? : boolean ) : CompilerHost {
2016-10-28 00:50:21 +02:00
const existingDirectories = createMap < boolean > ( ) ;
2014-12-16 22:14:14 +01:00
function getCanonicalFileName ( fileName : string ) : string {
// if underlying system can distinguish between two files whose names differs only in cases then file name already in canonical form.
// otherwise use toLowerCase as a canonical form.
return sys . useCaseSensitiveFileNames ? fileName : fileName.toLowerCase ( ) ;
}
2015-06-26 01:24:41 +02:00
2015-02-04 01:08:46 +01:00
function getSourceFile ( fileName : string , languageVersion : ScriptTarget , onError ? : ( message : string ) = > void ) : SourceFile {
2015-03-13 23:03:17 +01:00
let text : string ;
2014-12-16 22:14:14 +01:00
try {
2016-08-15 20:07:49 +02:00
performance . mark ( "beforeIORead" ) ;
2015-03-13 23:03:17 +01:00
text = sys . readFile ( fileName , options . charset ) ;
2016-08-15 20:07:49 +02:00
performance . mark ( "afterIORead" ) ;
performance . measure ( "I/O Read" , "beforeIORead" , "afterIORead" ) ;
2014-12-16 22:14:14 +01:00
}
catch ( e ) {
if ( onError ) {
2017-03-10 16:10:58 +01:00
onError ( e . message ) ;
2014-12-16 22:14:14 +01:00
}
text = "" ;
}
2015-04-12 23:02:58 +02:00
2015-03-18 22:11:50 +01:00
return text !== undefined ? createSourceFile ( fileName , text , languageVersion , setParentNodes ) : undefined ;
2014-12-16 22:14:14 +01:00
}
2015-03-13 22:49:32 +01:00
function directoryExists ( directoryPath : string ) : boolean {
2016-12-05 23:13:32 +01:00
if ( existingDirectories . has ( directoryPath ) ) {
2015-03-13 22:49:32 +01:00
return true ;
2014-12-16 22:14:14 +01:00
}
2015-03-13 22:49:32 +01:00
if ( sys . directoryExists ( directoryPath ) ) {
2016-12-05 23:13:32 +01:00
existingDirectories . set ( directoryPath , true ) ;
2015-03-13 22:49:32 +01:00
return true ;
2014-12-16 22:14:14 +01:00
}
2015-03-13 22:49:32 +01:00
return false ;
}
2014-12-16 22:14:14 +01:00
2015-03-13 22:49:32 +01:00
function ensureDirectoriesExist ( directoryPath : string ) {
if ( directoryPath . length > getRootLength ( directoryPath ) && ! directoryExists ( directoryPath ) ) {
2015-11-04 23:02:33 +01:00
const parentDirectory = getDirectoryPath ( directoryPath ) ;
2015-03-13 22:49:32 +01:00
ensureDirectoriesExist ( parentDirectory ) ;
sys . createDirectory ( directoryPath ) ;
2014-12-16 22:14:14 +01:00
}
2015-03-13 22:49:32 +01:00
}
2014-12-16 22:14:14 +01:00
2016-10-28 00:50:21 +02:00
let outputFingerprints : Map < OutputFingerprint > ;
2016-02-11 09:38:21 +01:00
function writeFileIfUpdated ( fileName : string , data : string , writeByteOrderMark : boolean ) : void {
if ( ! outputFingerprints ) {
2016-10-28 00:50:21 +02:00
outputFingerprints = createMap < OutputFingerprint > ( ) ;
2016-02-11 09:38:21 +01:00
}
const hash = sys . createHash ( data ) ;
const mtimeBefore = sys . getModifiedTime ( fileName ) ;
2016-12-05 23:13:32 +01:00
if ( mtimeBefore ) {
const fingerprint = outputFingerprints . get ( fileName ) ;
2016-02-11 09:38:21 +01:00
// If output has not been changed, and the file has no external modification
2016-12-05 23:13:32 +01:00
if ( fingerprint &&
fingerprint . byteOrderMark === writeByteOrderMark &&
2016-02-11 09:38:21 +01:00
fingerprint . hash === hash &&
fingerprint . mtime . getTime ( ) === mtimeBefore . getTime ( ) ) {
return ;
2016-02-09 15:23:43 +01:00
}
2016-02-11 09:38:21 +01:00
}
2016-02-09 15:23:43 +01:00
2016-02-11 09:38:21 +01:00
sys . writeFile ( fileName , data , writeByteOrderMark ) ;
2016-02-09 15:23:43 +01:00
2016-02-11 09:38:21 +01:00
const mtimeAfter = sys . getModifiedTime ( fileName ) ;
2016-02-09 15:23:43 +01:00
2016-12-05 23:13:32 +01:00
outputFingerprints . set ( fileName , {
2016-02-11 09:38:21 +01:00
hash ,
byteOrderMark : writeByteOrderMark ,
mtime : mtimeAfter
2016-12-05 23:13:32 +01:00
} ) ;
2016-02-11 09:38:21 +01:00
}
2016-02-09 15:23:43 +01:00
2015-03-13 22:49:32 +01:00
function writeFile ( fileName : string , data : string , writeByteOrderMark : boolean , onError ? : ( message : string ) = > void ) {
2014-12-16 22:14:14 +01:00
try {
2016-08-15 20:07:49 +02:00
performance . mark ( "beforeIOWrite" ) ;
2014-12-16 22:14:14 +01:00
ensureDirectoriesExist ( getDirectoryPath ( normalizePath ( fileName ) ) ) ;
2016-02-11 09:38:21 +01:00
2016-04-04 23:51:16 +02:00
if ( isWatchSet ( options ) && sys . createHash && sys . getModifiedTime ) {
2016-02-11 09:38:21 +01:00
writeFileIfUpdated ( fileName , data , writeByteOrderMark ) ;
}
else {
sys . writeFile ( fileName , data , writeByteOrderMark ) ;
}
2016-08-15 20:07:49 +02:00
performance . mark ( "afterIOWrite" ) ;
performance . measure ( "I/O Write" , "beforeIOWrite" , "afterIOWrite" ) ;
2014-12-16 22:14:14 +01:00
}
catch ( e ) {
if ( onError ) {
onError ( e . message ) ;
}
}
}
2016-03-29 01:24:16 +02:00
function getDefaultLibLocation ( ) : string {
return getDirectoryPath ( normalizePath ( sys . getExecutingFilePath ( ) ) ) ;
2016-03-28 23:20:29 +02:00
}
2015-05-27 05:18:13 +02:00
const newLine = getNewLineCharacter ( options ) ;
2016-05-05 22:38:09 +02:00
const realpath = sys . realpath && ( ( path : string ) = > sys . realpath ( path ) ) ;
2015-10-01 01:10:52 +02:00
2014-12-16 22:14:14 +01:00
return {
getSourceFile ,
2016-03-29 01:24:16 +02:00
getDefaultLibLocation ,
2016-03-30 19:26:39 +02:00
getDefaultLibFileName : options = > combinePaths ( getDefaultLibLocation ( ) , getDefaultLibFileName ( options ) ) ,
2014-12-16 22:14:14 +01:00
writeFile ,
2015-10-15 23:43:51 +02:00
getCurrentDirectory : memoize ( ( ) = > sys . getCurrentDirectory ( ) ) ,
2014-12-16 22:14:14 +01:00
useCaseSensitiveFileNames : ( ) = > sys . useCaseSensitiveFileNames ,
getCanonicalFileName ,
2015-07-14 02:44:50 +02:00
getNewLine : ( ) = > newLine ,
2015-08-05 06:22:37 +02:00
fileExists : fileName = > sys . fileExists ( fileName ) ,
2016-01-06 21:37:52 +01:00
readFile : fileName = > sys . readFile ( fileName ) ,
2016-01-06 23:16:56 +01:00
trace : ( s : string ) = > sys . write ( s + newLine ) ,
2016-05-05 22:38:09 +02:00
directoryExists : directoryName = > sys . directoryExists ( directoryName ) ,
2016-10-04 23:00:45 +02:00
getEnvironmentVariable : name = > sys . getEnvironmentVariable ? sys . getEnvironmentVariable ( name ) : "" ,
2016-06-11 00:44:11 +02:00
getDirectories : ( path : string ) = > sys . getDirectories ( path ) ,
2016-05-05 22:38:09 +02:00
realpath
2014-12-16 22:14:14 +01:00
} ;
}
2015-06-18 21:04:26 +02:00
export function getPreEmitDiagnostics ( program : Program , sourceFile? : SourceFile , cancellationToken? : CancellationToken ) : Diagnostic [ ] {
2017-08-24 18:55:01 +02:00
const diagnostics = [
. . . program . getOptionsDiagnostics ( cancellationToken ) ,
. . . program . getSyntacticDiagnostics ( sourceFile , cancellationToken ) ,
. . . program . getGlobalDiagnostics ( cancellationToken ) ,
. . . program . getSemanticDiagnostics ( sourceFile , cancellationToken )
] ;
2015-03-20 00:55:07 +01:00
if ( program . getCompilerOptions ( ) . declaration ) {
2017-08-24 18:55:01 +02:00
addRange ( diagnostics , program . getDeclarationDiagnostics ( sourceFile , cancellationToken ) ) ;
2015-03-20 00:55:07 +01:00
}
2015-02-05 10:47:29 +01:00
return sortAndDeduplicateDiagnostics ( diagnostics ) ;
}
2016-07-15 08:02:56 +02:00
export interface FormatDiagnosticsHost {
getCurrentDirectory ( ) : string ;
getCanonicalFileName ( fileName : string ) : string ;
getNewLine ( ) : string ;
}
2017-08-24 18:55:01 +02:00
export function formatDiagnostics ( diagnostics : ReadonlyArray < Diagnostic > , host : FormatDiagnosticsHost ) : string {
2016-07-13 18:13:55 +02:00
let output = "" ;
for ( const diagnostic of diagnostics ) {
2017-08-04 04:14:47 +02:00
output += formatDiagnostic ( diagnostic , host ) ;
2016-07-13 18:13:55 +02:00
}
return output ;
}
2017-08-04 04:14:47 +02:00
export function formatDiagnostic ( diagnostic : Diagnostic , host : FormatDiagnosticsHost ) : string {
2018-02-28 20:16:32 +01:00
const errorMessage = ` ${ diagnosticCategoryName ( diagnostic ) } TS ${ diagnostic . code } : ${ flattenDiagnosticMessageText ( diagnostic . messageText , host . getNewLine ( ) ) } ${ host . getNewLine ( ) } ` ;
2017-08-04 04:14:47 +02:00
if ( diagnostic . file ) {
const { line , character } = getLineAndCharacterOfPosition ( diagnostic . file , diagnostic . start ) ;
const fileName = diagnostic . file . fileName ;
const relativeFileName = convertToRelativePath ( fileName , host . getCurrentDirectory ( ) , fileName = > host . getCanonicalFileName ( fileName ) ) ;
return ` ${ relativeFileName } ( ${ line + 1 } , ${ character + 1 } ): ` + errorMessage ;
}
return errorMessage ;
}
2017-12-03 05:56:35 +01:00
/** @internal */
2017-12-19 21:19:39 +01:00
export enum ForegroundColorEscapeSequences {
Grey = "\u001b[90m" ,
Red = "\u001b[91m" ,
Yellow = "\u001b[93m" ,
Blue = "\u001b[94m" ,
Cyan = "\u001b[96m"
}
2017-08-08 04:08:48 +02:00
const gutterStyleSequence = "\u001b[30;47m" ;
2017-05-13 01:17:32 +02:00
const gutterSeparator = " " ;
const resetEscapeSequence = "\u001b[0m" ;
const ellipsis = "..." ;
function getCategoryFormat ( category : DiagnosticCategory ) : string {
switch ( category ) {
2017-12-19 21:19:39 +01:00
case DiagnosticCategory.Error : return ForegroundColorEscapeSequences . Red ;
2018-02-28 20:16:32 +01:00
case DiagnosticCategory.Warning : return ForegroundColorEscapeSequences . Yellow ;
case DiagnosticCategory.Suggestion : return Debug . fail ( "Should never get an Info diagnostic on the command line." ) ;
2017-12-19 21:19:39 +01:00
case DiagnosticCategory.Message : return ForegroundColorEscapeSequences . Blue ;
2017-05-13 01:17:32 +02:00
}
}
2017-12-03 05:56:35 +01:00
/** @internal */
export function formatColorAndReset ( text : string , formatStyle : string ) {
2017-05-13 01:17:32 +02:00
return formatStyle + text + resetEscapeSequence ;
}
function padLeft ( s : string , length : number ) {
while ( s . length < length ) {
s = " " + s ;
}
return s ;
}
2017-09-07 21:26:23 +02:00
export function formatDiagnosticsWithColorAndContext ( diagnostics : ReadonlyArray < Diagnostic > , host : FormatDiagnosticsHost ) : string {
2017-05-13 01:17:32 +02:00
let output = "" ;
for ( const diagnostic of diagnostics ) {
2017-06-17 03:59:40 +02:00
let context = "" ;
2017-05-13 01:17:32 +02:00
if ( diagnostic . file ) {
const { start , length , file } = diagnostic ;
const { line : firstLine , character : firstLineChar } = getLineAndCharacterOfPosition ( file , start ) ;
const { line : lastLine , character : lastLineChar } = getLineAndCharacterOfPosition ( file , start + length ) ;
const lastLineInFile = getLineAndCharacterOfPosition ( file , file . text . length ) . line ;
const relativeFileName = host ? convertToRelativePath ( file . fileName , host . getCurrentDirectory ( ) , fileName = > host . getCanonicalFileName ( fileName ) ) : file . fileName ;
const hasMoreThanFiveLines = ( lastLine - firstLine ) >= 4 ;
let gutterWidth = ( lastLine + 1 + "" ) . length ;
if ( hasMoreThanFiveLines ) {
gutterWidth = Math . max ( ellipsis . length , gutterWidth ) ;
}
for ( let i = firstLine ; i <= lastLine ; i ++ ) {
2018-02-21 23:07:58 +01:00
context += host . getNewLine ( ) ;
2017-05-13 01:17:32 +02:00
// If the error spans over 5 lines, we'll only show the first 2 and last 2 lines,
// so we'll skip ahead to the second-to-last line.
if ( hasMoreThanFiveLines && firstLine + 1 < i && i < lastLine - 1 ) {
2017-12-03 05:56:35 +01:00
context += formatColorAndReset ( padLeft ( ellipsis , gutterWidth ) , gutterStyleSequence ) + gutterSeparator + host . getNewLine ( ) ;
2017-05-13 01:17:32 +02:00
i = lastLine - 1 ;
}
const lineStart = getPositionOfLineAndCharacter ( file , i , 0 ) ;
const lineEnd = i < lastLineInFile ? getPositionOfLineAndCharacter ( file , i + 1 , 0 ) : file . text . length ;
let lineContent = file . text . slice ( lineStart , lineEnd ) ;
lineContent = lineContent . replace ( /\s+$/g , "" ) ; // trim from end
lineContent = lineContent . replace ( "\t" , " " ) ; // convert tabs to single spaces
// Output the gutter and the actual contents of the line.
2017-12-03 05:56:35 +01:00
context += formatColorAndReset ( padLeft ( i + 1 + "" , gutterWidth ) , gutterStyleSequence ) + gutterSeparator ;
2017-06-17 03:59:40 +02:00
context += lineContent + host . getNewLine ( ) ;
2017-05-13 01:17:32 +02:00
// Output the gutter and the error span for the line using tildes.
2017-12-03 05:56:35 +01:00
context += formatColorAndReset ( padLeft ( "" , gutterWidth ) , gutterStyleSequence ) + gutterSeparator ;
2017-12-19 21:19:39 +01:00
context += ForegroundColorEscapeSequences . Red ;
2017-05-13 01:17:32 +02:00
if ( i === firstLine ) {
// If we're on the last line, then limit it to the last character of the last line.
// Otherwise, we'll just squiggle the rest of the line, giving 'slice' no end position.
const lastCharForLine = i === lastLine ? lastLineChar : undefined ;
2017-06-17 03:59:40 +02:00
context += lineContent . slice ( 0 , firstLineChar ) . replace ( /\S/g , " " ) ;
context += lineContent . slice ( firstLineChar , lastCharForLine ) . replace ( /./g , "~" ) ;
2017-05-13 01:17:32 +02:00
}
else if ( i === lastLine ) {
2017-06-17 03:59:40 +02:00
context += lineContent . slice ( 0 , lastLineChar ) . replace ( /./g , "~" ) ;
2017-05-13 01:17:32 +02:00
}
else {
// Squiggle the entire line.
2017-06-17 03:59:40 +02:00
context += lineContent . replace ( /./g , "~" ) ;
2017-05-13 01:17:32 +02:00
}
2017-06-17 03:59:40 +02:00
context += resetEscapeSequence ;
2017-05-13 01:17:32 +02:00
}
2017-12-19 21:19:39 +01:00
output += formatColorAndReset ( relativeFileName , ForegroundColorEscapeSequences . Cyan ) ;
2018-01-05 01:38:52 +01:00
output += ":" ;
2017-12-19 21:19:39 +01:00
output += formatColorAndReset ( ` ${ firstLine + 1 } ` , ForegroundColorEscapeSequences . Yellow ) ;
2018-01-05 01:38:52 +01:00
output += ":" ;
2017-12-19 21:19:39 +01:00
output += formatColorAndReset ( ` ${ firstLineChar + 1 } ` , ForegroundColorEscapeSequences . Yellow ) ;
2018-01-05 01:38:52 +01:00
output += " - " ;
2017-05-13 01:17:32 +02:00
}
2018-02-28 20:16:32 +01:00
output += formatColorAndReset ( diagnosticCategoryName ( diagnostic ) , getCategoryFormat ( diagnostic . category ) ) ;
2017-12-19 21:19:39 +01:00
output += formatColorAndReset ( ` TS ${ diagnostic . code } : ` , ForegroundColorEscapeSequences . Grey ) ;
2017-12-03 05:56:35 +01:00
output += flattenDiagnosticMessageText ( diagnostic . messageText , host . getNewLine ( ) ) ;
2017-06-17 03:59:40 +02:00
if ( diagnostic . file ) {
output += host . getNewLine ( ) ;
output += context ;
}
2017-09-07 21:26:23 +02:00
output += host . getNewLine ( ) ;
2017-05-13 01:17:32 +02:00
}
2017-12-03 05:56:35 +01:00
return output + host . getNewLine ( ) ;
2016-07-13 18:13:55 +02:00
}
2015-02-05 10:47:29 +01:00
export function flattenDiagnosticMessageText ( messageText : string | DiagnosticMessageChain , newLine : string ) : string {
2017-07-19 22:07:55 +02:00
if ( isString ( messageText ) ) {
2015-02-05 10:47:29 +01:00
return messageText ;
}
else {
2015-03-13 23:03:17 +01:00
let diagnosticChain = messageText ;
let result = "" ;
2015-02-05 10:47:29 +01:00
2015-03-13 23:03:17 +01:00
let indent = 0 ;
2015-02-05 10:47:29 +01:00
while ( diagnosticChain ) {
if ( indent ) {
result += newLine ;
2015-03-13 23:03:17 +01:00
for ( let i = 0 ; i < indent ; i ++ ) {
2015-02-05 10:47:29 +01:00
result += " " ;
}
}
result += diagnosticChain . messageText ;
indent ++ ;
diagnosticChain = diagnosticChain . next ;
}
return result ;
}
}
2016-04-01 21:41:01 +02:00
function loadWithLocalCache < T > ( names : string [ ] , containingFile : string , loader : ( name : string , containingFile : string ) = > T ) : T [ ] {
if ( names . length === 0 ) {
return [ ] ;
}
const resolutions : T [ ] = [ ] ;
2016-10-28 00:50:21 +02:00
const cache = createMap < T > ( ) ;
2016-04-01 21:41:01 +02:00
for ( const name of names ) {
2016-12-12 17:42:12 +01:00
let result : T ;
if ( cache . has ( name ) ) {
result = cache . get ( name ) ;
}
else {
cache . set ( name , result = loader ( name , containingFile ) ) ;
}
2016-04-01 21:41:01 +02:00
resolutions . push ( result ) ;
}
return resolutions ;
}
2017-03-07 22:26:41 +01:00
interface DiagnosticCache {
2017-06-28 22:15:34 +02:00
perFile? : Map < Diagnostic [ ] > ;
2017-03-07 22:26:41 +01:00
allDiagnostics? : Diagnostic [ ] ;
}
2017-09-26 22:34:56 +02:00
/ * *
* Determines if program structure is upto date or needs to be recreated
* /
2017-10-03 04:08:13 +02:00
/* @internal */
2017-08-30 20:49:58 +02:00
export function isProgramUptoDate (
program : Program | undefined ,
rootFileNames : string [ ] ,
newOptions : CompilerOptions ,
getSourceVersion : ( path : Path ) = > string ,
fileExists : ( fileName : string ) = > boolean ,
hasInvalidatedResolution : HasInvalidatedResolution ,
2017-09-26 22:34:56 +02:00
hasChangedAutomaticTypeDirectiveNames : boolean ,
2017-08-30 20:49:58 +02:00
) : boolean {
2018-02-01 19:58:31 +01:00
// If we haven't created a program yet or have changed automatic type directives, then it is not up-to-date
2017-09-26 22:34:56 +02:00
if ( ! program || hasChangedAutomaticTypeDirectiveNames ) {
2017-07-25 01:57:49 +02:00
return false ;
}
// If number of files in the program do not match, it is not up-to-date
if ( program . getRootFileNames ( ) . length !== rootFileNames . length ) {
return false ;
}
// If any file is not up-to-date, then the whole program is not up-to-date
2017-08-15 00:52:20 +02:00
if ( program . getSourceFiles ( ) . some ( sourceFileNotUptoDate ) ) {
2017-09-26 22:34:56 +02:00
return false ;
2017-07-25 01:57:49 +02:00
}
2017-07-27 00:12:03 +02:00
// If any of the missing file paths are now created
2017-08-15 00:52:20 +02:00
if ( program . getMissingFilePaths ( ) . some ( fileExists ) ) {
2017-07-27 00:12:03 +02:00
return false ;
}
2017-07-25 01:57:49 +02:00
const currentOptions = program . getCompilerOptions ( ) ;
// If the compilation settings do no match, then the program is not up-to-date
if ( ! compareDataObjects ( currentOptions , newOptions ) ) {
return false ;
}
// If everything matches but the text of config file is changed,
// error locations can change for program options, so update the program
if ( currentOptions . configFile && newOptions . configFile ) {
return currentOptions . configFile . text === newOptions . configFile . text ;
}
return true ;
2017-08-15 00:52:20 +02:00
function sourceFileNotUptoDate ( sourceFile : SourceFile ) : boolean {
return sourceFile . version !== getSourceVersion ( sourceFile . path ) ||
hasInvalidatedResolution ( sourceFile . path ) ;
2017-07-25 01:57:49 +02:00
}
}
2017-08-15 00:52:20 +02:00
/ * *
2018-02-01 19:58:31 +01:00
* Determined if source file needs to be re - created even if its text hasn ' t changed
2017-08-15 00:52:20 +02:00
* /
2017-07-25 01:57:49 +02:00
function shouldProgramCreateNewSourceFiles ( program : Program , newOptions : CompilerOptions ) {
2018-02-01 19:58:31 +01:00
// If any of these options change, we can't reuse old source file even if version match
2017-08-15 00:52:20 +02:00
// The change in options like these could result in change in syntax tree change
2017-07-25 01:57:49 +02:00
const oldOptions = program && program . getCompilerOptions ( ) ;
2017-09-11 22:40:00 +02:00
return oldOptions && (
oldOptions . target !== newOptions . target ||
oldOptions . module !== newOptions . module ||
oldOptions . module Resolution !== newOptions . module Resolution ||
oldOptions . noResolve !== newOptions . noResolve ||
oldOptions . jsx !== newOptions . jsx ||
oldOptions . allowJs !== newOptions . allowJs ||
oldOptions . disableSizeLimit !== newOptions . disableSizeLimit ||
oldOptions . baseUrl !== newOptions . baseUrl ||
! equalOwnProperties ( oldOptions . paths , newOptions . paths )
) ;
2017-07-25 01:57:49 +02:00
}
2017-05-23 19:40:46 +02:00
/ * *
* Create a new 'Program' instance . A Program is an immutable collection of 'SourceFile' s and a 'CompilerOptions'
* that represent a compilation unit .
*
* Creating a program proceeds from a set of root files , expanding the set of inputs by following imports and
* triple - slash - reference - path directives transitively . '@types' and triple - slash - reference - types are also pulled in .
*
* @param rootNames - A set of root files .
* @param options - The compiler options which should be used .
* @param host - The host interacts with the underlying file system .
* @param oldProgram - Reuses an old program structure .
* @returns A 'Program' object .
* /
2017-08-24 18:55:01 +02:00
export function createProgram ( rootNames : ReadonlyArray < string > , options : CompilerOptions , host? : CompilerHost , oldProgram? : Program ) : Program {
2015-03-13 23:03:17 +01:00
let program : Program ;
let files : SourceFile [ ] = [ ] ;
2015-11-18 23:10:53 +01:00
let commonSourceDirectory : string ;
2015-03-17 14:26:24 +01:00
let diagnosticsProducingTypeChecker : TypeChecker ;
let noDiagnosticsTypeChecker : TypeChecker ;
2017-07-07 19:34:36 +02:00
let classifiableNames : UnderscoreEscapedMap < true > ;
2017-04-19 00:48:01 +02:00
let modifiedFilePaths : Path [ ] | undefined ;
2014-12-16 22:14:14 +01:00
2017-03-17 21:37:12 +01:00
const cachedSemanticDiagnosticsForFile : DiagnosticCache = { } ;
const cachedDeclarationDiagnosticsForFile : DiagnosticCache = { } ;
2017-03-07 22:26:41 +01:00
2016-10-28 00:50:21 +02:00
let resolvedTypeReferenceDirectives = createMap < ResolvedTypeReferenceDirective > ( ) ;
2016-04-01 21:41:01 +02:00
let fileProcessingDiagnostics = createDiagnosticCollection ( ) ;
2015-05-01 03:14:53 +02:00
2016-06-21 22:42:02 +02:00
// The below settings are to track if a .js file should be add to the program if loaded via searching under node_modules.
// This works as imported modules are discovered recursively in a depth first manner, specifically:
// - For each root file, findSourceFile is called.
// - This calls processImportedModules for each module imported in the source file.
// - This calls resolveModuleNames, and then calls findSourceFile for each resolved module.
// As all these operations happen - and are nested - within the createProgram call, they close over the below variables.
// The current resolution depth is tracked by incrementing/decrementing as the depth first search progresses.
2016-10-13 22:02:22 +02:00
const maxNodeModuleJsDepth = typeof options . maxNodeModuleJsDepth === "number" ? options.maxNodeModuleJsDepth : 0 ;
2016-07-11 01:11:42 +02:00
let currentNodeModulesDepth = 0 ;
2016-06-21 22:42:02 +02:00
2016-06-27 05:48:22 +02:00
// If a module has some of its imports skipped due to being at the depth limit under node_modules, then track
// this, as it may be imported at a shallower depth later, and then it will need its skipped imports processed.
2016-10-28 00:50:21 +02:00
const module sWithElidedImports = createMap < boolean > ( ) ;
2016-06-27 05:48:22 +02:00
2016-07-11 01:11:42 +02:00
// Track source files that are source files found by searching under node_modules, as these shouldn't be compiled.
2016-10-28 00:50:21 +02:00
const sourceFilesFoundSearchingNodeModules = createMap < boolean > ( ) ;
2016-06-27 05:48:22 +02:00
2016-08-15 20:07:49 +02:00
performance . mark ( "beforeProgram" ) ;
2014-12-16 22:14:14 +01:00
2015-03-17 14:26:24 +01:00
host = host || createCompilerHost ( options ) ;
2016-04-01 21:41:01 +02:00
2016-04-11 05:42:22 +02:00
let skipDefaultLib = options . noLib ;
2017-08-16 03:44:28 +02:00
const getDefaultLibraryFileName = memoize ( ( ) = > host . getDefaultLibFileName ( options ) ) ;
const defaultLibraryPath = host . getDefaultLibLocation ? host . getDefaultLibLocation ( ) : getDirectoryPath ( getDefaultLibraryFileName ( ) ) ;
2016-04-11 05:42:22 +02:00
const programDiagnostics = createDiagnosticCollection ( ) ;
const currentDirectory = host . getCurrentDirectory ( ) ;
const supportedExtensions = getSupportedExtensions ( options ) ;
2015-10-30 23:54:31 +01:00
// Map storing if there is emit blocking diagnostics for given input
2017-07-10 20:24:17 +02:00
const hasEmitBlockingDiagnostics = createMap < boolean > ( ) ;
2016-11-29 21:42:17 +01:00
let _compilerOptionsObjectLiteralSyntax : ObjectLiteralExpression ;
2015-10-01 01:10:52 +02:00
2016-12-19 22:48:45 +01:00
let module ResolutionCache : ModuleResolutionCache ;
2017-09-12 21:09:06 +02:00
let resolveModuleNamesWorker : ( module Names : string [ ] , containingFile : string , reusedNames? : string [ ] ) = > ResolvedModuleFull [ ] ;
2017-08-14 21:52:29 +02:00
const hasInvalidatedResolution = host . hasInvalidatedResolution || returnFalse ;
2016-04-01 21:41:01 +02:00
if ( host . resolveModuleNames ) {
2017-09-12 21:09:06 +02:00
resolveModuleNamesWorker = ( module Names , containingFile , reusedNames ) = > host . resolveModuleNames ( checkAllDefined ( module Names ) , containingFile , reusedNames ) . map ( resolved = > {
2016-10-25 21:38:59 +02:00
// An older host may have omitted extension, in which case we should infer it from the file extension of resolvedFileName.
2016-10-27 20:33:01 +02:00
if ( ! resolved || ( resolved as ResolvedModuleFull ) . extension !== undefined ) {
return resolved as ResolvedModuleFull ;
2016-10-25 21:38:59 +02:00
}
2016-10-27 20:33:01 +02:00
const withExtension = clone ( resolved ) as ResolvedModuleFull ;
withExtension . extension = extensionFromPath ( resolved . resolvedFileName ) ;
return withExtension ;
2016-10-25 21:38:59 +02:00
} ) ;
2016-04-01 21:41:01 +02:00
}
else {
2016-12-19 22:48:45 +01:00
module ResolutionCache = createModuleResolutionCache ( currentDirectory , x = > host . getCanonicalFileName ( x ) ) ;
const loader = ( module Name : string , containingFile : string ) = > resolveModuleName ( module Name , containingFile , options , host , module ResolutionCache ) . resolvedModule ;
2017-07-12 19:13:33 +02:00
resolveModuleNamesWorker = ( module Names , containingFile ) = > loadWithLocalCache ( checkAllDefined ( module Names ) , containingFile , loader ) ;
2016-04-01 21:41:01 +02:00
}
let resolveTypeReferenceDirectiveNamesWorker : ( typeDirectiveNames : string [ ] , containingFile : string ) = > ResolvedTypeReferenceDirective [ ] ;
if ( host . resolveTypeReferenceDirectives ) {
2017-07-12 19:13:33 +02:00
resolveTypeReferenceDirectiveNamesWorker = ( typeDirectiveNames , containingFile ) = > host . resolveTypeReferenceDirectives ( checkAllDefined ( typeDirectiveNames ) , containingFile ) ;
2016-04-01 21:41:01 +02:00
}
else {
2016-04-06 01:33:11 +02:00
const loader = ( typesRef : string , containingFile : string ) = > resolveTypeReferenceDirective ( typesRef , containingFile , options , host ) . resolvedTypeReferenceDirective ;
2017-07-12 19:13:33 +02:00
resolveTypeReferenceDirectiveNamesWorker = ( typeReferenceDirectiveNames , containingFile ) = > loadWithLocalCache ( checkAllDefined ( typeReferenceDirectiveNames ) , containingFile , loader ) ;
2016-04-01 21:41:01 +02:00
}
2015-05-01 03:14:53 +02:00
2017-08-09 23:39:06 +02:00
// Map from a stringified PackageId to the source file with that id.
// Only one source file may have a given packageId. Others become redirects (see createRedirectSourceFile).
// `packageIdToSourceFile` is only used while building the program, while `sourceFileToPackageName` and `isSourceFileTargetOfRedirect` are kept around.
const packageIdToSourceFile = createMap < SourceFile > ( ) ;
// Maps from a SourceFile's `.path` to the name of the package it was imported with.
let sourceFileToPackageName = createMap < string > ( ) ;
// See `sourceFileIsRedirectedTo`.
let redirectTargetsSet = createMap < true > ( ) ;
2017-06-22 22:47:54 +02:00
const filesByName = createMap < SourceFile | undefined > ( ) ;
2017-08-15 00:52:20 +02:00
let missingFilePaths : ReadonlyArray < Path > ;
2015-10-15 23:43:51 +02:00
// stores 'filename -> file association' ignoring case
2016-01-30 20:37:02 +01:00
// used to track cases when two file names differ only in casing
2017-07-10 20:24:17 +02:00
const filesByNameIgnoreCase = host . useCaseSensitiveFileNames ( ) ? createMap < SourceFile > ( ) : undefined ;
2015-10-01 01:10:52 +02:00
2017-07-25 01:57:49 +02:00
const shouldCreateNewSourceFile = shouldProgramCreateNewSourceFiles ( oldProgram , options ) ;
2017-04-22 01:54:42 +02:00
const structuralIsReused = tryReuseStructureFromOldProgram ( ) ;
if ( structuralIsReused !== StructureIsReused . Completely ) {
2016-05-18 20:30:40 +02:00
forEach ( rootNames , name = > processRootFile ( name , /*isDefaultLib*/ false ) ) ;
2016-06-11 00:44:11 +02:00
// load type declarations specified via 'types' argument or implicitly from types/ and node_modules/@types folders
2016-08-04 16:43:54 +02:00
const typeReferences : string [ ] = getAutomaticTypeDirectiveNames ( options , host ) ;
2016-05-18 00:41:31 +02:00
2016-10-07 17:31:53 +02:00
if ( typeReferences . length ) {
2016-08-04 16:43:54 +02:00
// This containingFilename needs to match with the one used in managed-side
2016-11-18 22:39:11 +01:00
const containingDirectory = options . configFilePath ? getDirectoryPath ( options . configFilePath ) : host . getCurrentDirectory ( ) ;
const containingFilename = combinePaths ( containingDirectory , "__inferred type names__.ts" ) ;
2016-06-11 00:44:11 +02:00
const resolutions = resolveTypeReferenceDirectiveNamesWorker ( typeReferences , containingFilename ) ;
2016-05-18 00:41:31 +02:00
for ( let i = 0 ; i < typeReferences . length ; i ++ ) {
processTypeReferenceDirective ( typeReferences [ i ] , resolutions [ i ] ) ;
2016-04-06 22:49:25 +02:00
}
2015-06-25 02:40:04 +02:00
}
2015-10-01 01:10:52 +02:00
2015-06-23 02:48:44 +02:00
// Do not process the default library if:
// - The '--noLib' flag is used.
// - A 'no-default-lib' reference comment is encountered in
// processing the root files.
if ( ! skipDefaultLib ) {
2016-03-28 23:20:29 +02:00
// If '--lib' is not specified, include default library file according to '--target'
// otherwise, using options specified in '--lib' instead of '--target' default library file
if ( ! options . lib ) {
2017-08-16 03:44:28 +02:00
processRootFile ( getDefaultLibraryFileName ( ) , /*isDefaultLib*/ true ) ;
2016-03-28 23:20:29 +02:00
}
else {
2016-03-29 01:24:16 +02:00
forEach ( options . lib , libFileName = > {
2017-08-16 03:44:28 +02:00
processRootFile ( combinePaths ( defaultLibraryPath , libFileName ) , /*isDefaultLib*/ true ) ;
2016-03-28 23:20:29 +02:00
} ) ;
}
2015-06-23 02:48:44 +02:00
}
2015-05-01 03:14:53 +02:00
2017-07-21 23:58:06 +02:00
missingFilePaths = arrayFrom ( filesByName . keys ( ) , p = > < Path > p ) . filter ( p = > ! filesByName . get ( p ) ) ;
2014-12-16 22:14:14 +01:00
}
2015-05-01 03:14:53 +02:00
2017-08-15 00:52:20 +02:00
Debug . assert ( ! ! missingFilePaths ) ;
2017-06-21 00:54:43 +02:00
2016-12-19 22:48:45 +01:00
// unconditionally set moduleResolutionCache to undefined to avoid unnecessary leaks
module ResolutionCache = undefined ;
2017-07-25 01:57:49 +02:00
// Release any files we have acquired in the old program but are
// not part of the new program.
if ( oldProgram && host . onReleaseOldSourceFile ) {
const oldSourceFiles = oldProgram . getSourceFiles ( ) ;
for ( const oldSourceFile of oldSourceFiles ) {
if ( ! getSourceFile ( oldSourceFile . path ) || shouldCreateNewSourceFile ) {
host . onReleaseOldSourceFile ( oldSourceFile , oldProgram . getCompilerOptions ( ) ) ;
}
}
}
2015-07-09 23:45:39 +02:00
// unconditionally set oldProgram to undefined to prevent it from being captured in closure
oldProgram = undefined ;
2014-12-16 22:14:14 +01:00
program = {
2015-06-23 02:48:44 +02:00
getRootFileNames : ( ) = > rootNames ,
2015-10-29 22:54:56 +01:00
getSourceFile ,
2016-05-11 08:43:26 +02:00
getSourceFileByPath ,
2014-12-16 22:14:14 +01:00
getSourceFiles : ( ) = > files ,
2017-06-21 00:54:43 +02:00
getMissingFilePaths : ( ) = > missingFilePaths ,
2014-12-16 22:14:14 +01:00
getCompilerOptions : ( ) = > options ,
2015-02-05 10:47:29 +01:00
getSyntacticDiagnostics ,
2015-06-18 18:32:52 +02:00
getOptionsDiagnostics ,
2015-02-04 23:29:25 +01:00
getGlobalDiagnostics ,
2015-02-05 10:47:29 +01:00
getSemanticDiagnostics ,
2015-02-04 23:29:25 +01:00
getDeclarationDiagnostics ,
2014-12-16 22:14:14 +01:00
getTypeChecker ,
2015-06-12 21:53:24 +02:00
getClassifiableNames ,
2015-02-05 01:11:38 +01:00
getDiagnosticsProducingTypeChecker ,
2015-11-19 02:10:22 +01:00
getCommonSourceDirectory ,
2015-02-05 01:53:14 +01:00
emit ,
2015-10-15 23:43:51 +02:00
getCurrentDirectory : ( ) = > currentDirectory ,
2015-02-05 01:11:38 +01:00
getNodeCount : ( ) = > getDiagnosticsProducingTypeChecker ( ) . getNodeCount ( ) ,
getIdentifierCount : ( ) = > getDiagnosticsProducingTypeChecker ( ) . getIdentifierCount ( ) ,
getSymbolCount : ( ) = > getDiagnosticsProducingTypeChecker ( ) . getSymbolCount ( ) ,
getTypeCount : ( ) = > getDiagnosticsProducingTypeChecker ( ) . getTypeCount ( ) ,
2016-04-01 21:41:01 +02:00
getFileProcessingDiagnostics : ( ) = > fileProcessingDiagnostics ,
2016-06-18 01:22:03 +02:00
getResolvedTypeReferenceDirectives : ( ) = > resolvedTypeReferenceDirectives ,
2016-11-08 06:13:11 +01:00
isSourceFileFromExternalLibrary ,
2017-07-25 23:21:57 +02:00
isSourceFileDefaultLibrary ,
2017-05-09 21:20:52 +02:00
dropDiagnosticsProducingTypeChecker ,
getSourceFileFromReference ,
2017-08-09 23:39:06 +02:00
sourceFileToPackageName ,
2018-01-11 20:52:46 +01:00
redirectTargetsSet ,
isEmittedFile
2014-12-16 22:14:14 +01:00
} ;
2015-10-12 21:25:13 +02:00
verifyCompilerOptions ( ) ;
2016-08-15 20:07:49 +02:00
performance . mark ( "afterProgram" ) ;
performance . measure ( "Program" , "beforeProgram" , "afterProgram" ) ;
2015-10-12 22:10:54 +02:00
2014-12-16 22:14:14 +01:00
return program ;
2017-07-10 20:24:17 +02:00
function toPath ( fileName : string ) : Path {
return ts . toPath ( fileName , currentDirectory , getCanonicalFileName ) ;
}
2015-11-19 02:10:22 +01:00
function getCommonSourceDirectory() {
2016-11-02 15:05:54 +01:00
if ( commonSourceDirectory === undefined ) {
2017-01-20 17:12:00 +01:00
const emittedFiles = filter ( files , file = > sourceFileMayBeEmitted ( file , options , isSourceFileFromExternalLibrary ) ) ;
2016-11-02 15:05:54 +01:00
if ( options . rootDir && checkSourceFilesBelongToPath ( emittedFiles , options . rootDir ) ) {
2015-11-19 02:10:22 +01:00
// If a rootDir is specified and is valid use it as the commonSourceDirectory
commonSourceDirectory = getNormalizedAbsolutePath ( options . rootDir , currentDirectory ) ;
}
else {
2016-11-02 15:05:54 +01:00
commonSourceDirectory = computeCommonSourceDirectory ( emittedFiles ) ;
2015-11-19 02:10:22 +01:00
}
if ( commonSourceDirectory && commonSourceDirectory [ commonSourceDirectory . length - 1 ] !== directorySeparator ) {
// Make sure directory path ends with directory separator so this string can directly
// used to replace with "" to get the relative path of the source file and the relative path doesn't
// start with / making it rooted path
commonSourceDirectory += directorySeparator ;
}
}
return commonSourceDirectory ;
}
2016-10-28 00:50:21 +02:00
function getClassifiableNames() {
2015-06-12 21:53:24 +02:00
if ( ! classifiableNames ) {
2015-06-11 03:18:37 +02:00
// Initialize a checker so that all our files are bound.
getTypeChecker ( ) ;
2017-07-07 19:34:36 +02:00
classifiableNames = createUnderscoreEscapedMap < true > ( ) ;
2016-10-28 00:50:21 +02:00
for ( const sourceFile of files ) {
2016-12-28 18:16:38 +01:00
copyEntries ( sourceFile . classifiableNames , classifiableNames ) ;
2016-10-28 00:50:21 +02:00
}
2015-06-11 03:18:37 +02:00
}
2015-06-12 21:53:24 +02:00
return classifiableNames ;
2015-06-11 03:18:37 +02:00
}
2016-11-02 22:41:26 +01:00
interface OldProgramState {
2017-04-19 00:48:01 +02:00
program : Program | undefined ;
2017-12-06 22:17:21 +01:00
oldSourceFile : SourceFile | undefined ;
2017-04-28 05:27:48 +02:00
/** The collection of paths modified *since* the old program. */
2016-11-02 22:41:26 +01:00
modifiedFilePaths : Path [ ] ;
}
2017-04-19 00:48:01 +02:00
function resolveModuleNamesReusingOldState ( module Names : string [ ] , containingFile : string , file : SourceFile , oldProgramState : OldProgramState ) {
2017-04-22 01:54:42 +02:00
if ( structuralIsReused === StructureIsReused . Not && ! file . ambientModuleNames . length ) {
2017-04-19 00:48:01 +02:00
// If the old program state does not permit reusing resolutions and `file` does not contain locally defined ambient modules,
// the best we can do is fallback to the default logic.
2016-11-02 22:41:26 +01:00
return resolveModuleNamesWorker ( module Names , containingFile ) ;
}
2017-04-29 01:27:41 +02:00
const oldSourceFile = oldProgramState . program && oldProgramState . program . getSourceFile ( containingFile ) ;
if ( oldSourceFile !== file && file . resolvedModules ) {
// `file` was created for the new program.
//
// We only set `file.resolvedModules` via work from the current function,
// so it is defined iff we already called the current function on `file`.
// That call happened no later than the creation of the `file` object,
// which per above occured during the current program creation.
2017-05-01 20:47:12 +02:00
// Since we assume the filesystem does not change during program creation,
2017-04-29 01:27:41 +02:00
// it is safe to reuse resolutions from the earlier call.
const result : ResolvedModuleFull [ ] = [ ] ;
for ( const module Name of module Names ) {
const resolvedModule = file . resolvedModules . get ( module Name ) ;
result . push ( resolvedModule ) ;
}
return result ;
}
// At this point, we know at least one of the following hold:
2016-11-02 22:41:26 +01:00
// - file has local declarations for ambient modules
// - old program state is available
2017-04-28 05:27:48 +02:00
// With this information, we can infer some module resolutions without performing resolution.
/** An ordered list of module names for which we cannot recover the resolution. */
2016-11-02 22:41:26 +01:00
let unknownModuleNames : string [ ] ;
2017-04-28 05:27:48 +02:00
/ * *
* The indexing of elements in this list matches that of ` moduleNames ` .
*
* Before combining results , result [ i ] is in one of the following states :
* * undefined : needs to be recomputed ,
* * predictedToResolveToAmbientModuleMarker : known to be an ambient module .
* Needs to be reset to undefined before returning ,
* * ResolvedModuleFull instance : can be reused .
* /
2016-11-02 22:41:26 +01:00
let result : ResolvedModuleFull [ ] ;
2017-09-12 21:09:06 +02:00
let reusedNames : string [ ] ;
2017-04-28 05:27:48 +02:00
/** A transient placeholder used to mark predicted resolution in the result list. */
2016-11-02 22:41:26 +01:00
const predictedToResolveToAmbientModuleMarker : ResolvedModuleFull = < any > { } ;
for ( let i = 0 ; i < module Names.length ; i ++ ) {
const module Name = module Names [ i ] ;
2017-10-02 21:22:05 +02:00
// If the source file is unchanged and doesnt have invalidated resolution, reuse the module resolutions
2017-09-09 00:14:03 +02:00
if ( file === oldSourceFile && ! hasInvalidatedResolution ( oldSourceFile . path ) ) {
2017-04-28 05:27:48 +02:00
const oldResolvedModule = oldSourceFile && oldSourceFile . resolvedModules . get ( module Name ) ;
if ( oldResolvedModule ) {
if ( isTraceEnabled ( options , host ) ) {
trace ( host , Diagnostics . Reusing_resolution_of_module_0_to_file_1_from_old_program , module Name , containingFile ) ;
}
( result || ( result = new Array ( module Names.length ) ) ) [ i ] = oldResolvedModule ;
2017-09-12 21:09:06 +02:00
( reusedNames || ( reusedNames = [ ] ) ) . push ( module Name ) ;
2017-04-28 05:27:48 +02:00
continue ;
}
}
// We know moduleName resolves to an ambient module provided that moduleName:
// - is in the list of ambient modules locally declared in the current source file.
// - resolved to an ambient module in the old program whose declaration is in an unmodified file
2016-11-02 22:41:26 +01:00
// (so the same module declaration will land in the new program)
2017-04-28 05:27:48 +02:00
let resolvesToAmbientModuleInNonModifiedFile = false ;
2016-11-02 22:41:26 +01:00
if ( contains ( file . ambientModuleNames , module Name ) ) {
2017-04-28 05:27:48 +02:00
resolvesToAmbientModuleInNonModifiedFile = true ;
2016-11-02 22:41:26 +01:00
if ( isTraceEnabled ( options , host ) ) {
trace ( host , Diagnostics . Module_0_was_resolved_as_locally_declared_ambient_module_in_file_1 , module Name , containingFile ) ;
}
}
else {
2017-04-28 05:27:48 +02:00
resolvesToAmbientModuleInNonModifiedFile = module NameResolvesToAmbientModuleInNonModifiedFile ( module Name , oldProgramState ) ;
2016-11-02 22:41:26 +01:00
}
2017-04-28 05:27:48 +02:00
if ( resolvesToAmbientModuleInNonModifiedFile ) {
( result || ( result = new Array ( module Names.length ) ) ) [ i ] = predictedToResolveToAmbientModuleMarker ;
2016-11-02 22:41:26 +01:00
}
2017-04-28 05:27:48 +02:00
else {
// Resolution failed in the old program, or resolved to an ambient module for which we can't reuse the result.
( unknownModuleNames || ( unknownModuleNames = [ ] ) ) . push ( module Name ) ;
2016-11-02 22:41:26 +01:00
}
}
2017-04-28 05:27:48 +02:00
const resolutions = unknownModuleNames && unknownModuleNames . length
2017-09-12 21:09:06 +02:00
? resolveModuleNamesWorker ( unknownModuleNames , containingFile , reusedNames )
2016-11-02 22:41:26 +01:00
: emptyArray ;
2017-04-28 05:27:48 +02:00
// Combine results of resolutions and predicted results
if ( ! result ) {
// There were no unresolved/ambient resolutions.
Debug . assert ( resolutions . length === module Names.length ) ;
2018-02-17 03:38:00 +01:00
return resolutions ;
2017-04-28 05:27:48 +02:00
}
2016-11-02 22:41:26 +01:00
let j = 0 ;
for ( let i = 0 ; i < result . length ; i ++ ) {
2017-04-28 05:27:48 +02:00
if ( result [ i ] ) {
2017-05-01 20:47:12 +02:00
// `result[i]` is either a `ResolvedModuleFull` or a marker.
// If it is the former, we can leave it as is.
2017-04-28 05:27:48 +02:00
if ( result [ i ] === predictedToResolveToAmbientModuleMarker ) {
result [ i ] = undefined ;
}
2016-11-02 22:41:26 +01:00
}
else {
result [ i ] = resolutions [ j ] ;
j ++ ;
}
}
Debug . assert ( j === resolutions . length ) ;
2017-04-28 05:27:48 +02:00
2016-11-02 22:41:26 +01:00
return result ;
2017-05-02 02:49:19 +02:00
// If we change our policy of rechecking failed lookups on each program create,
2017-04-28 05:27:48 +02:00
// we should adjust the value returned here.
function module NameResolvesToAmbientModuleInNonModifiedFile ( module Name : string , oldProgramState : OldProgramState ) : boolean {
2017-12-06 22:17:21 +01:00
const resolutionToFile = getResolvedModule ( oldProgramState . oldSourceFile , module Name ) ;
const resolvedFile = resolutionToFile && oldProgramState . program && oldProgramState . program . getSourceFile ( resolutionToFile . resolvedFileName ) ;
if ( resolutionToFile && resolvedFile && ! resolvedFile . externalModuleIndicator ) {
// In the old program, we resolved to an ambient module that was in the same
// place as we expected to find an actual module file.
// We actually need to return 'false' here even though this seems like a 'true' case
// because the normal module resolution algorithm will find this anyway.
2016-11-02 22:41:26 +01:00
return false ;
}
2017-04-19 00:48:01 +02:00
const ambientModule = oldProgramState . program && oldProgramState . program . getTypeChecker ( ) . tryFindAmbientModuleWithoutAugmentations ( module Name ) ;
2016-11-02 22:41:26 +01:00
if ( ! ( ambientModule && ambientModule . declarations ) ) {
return false ;
}
// at least one of declarations should come from non-modified source file
const firstUnmodifiedFile = forEach ( ambientModule . declarations , d = > {
const f = getSourceFileOfNode ( d ) ;
return ! contains ( oldProgramState . modifiedFilePaths , f . path ) && f ;
} ) ;
if ( ! firstUnmodifiedFile ) {
return false ;
}
if ( isTraceEnabled ( options , host ) ) {
trace ( host , Diagnostics . Module_0_was_resolved_as_ambient_module_declared_in_1_since_this_file_was_not_modified , module Name , firstUnmodifiedFile . fileName ) ;
}
return true ;
}
}
2017-04-22 01:54:42 +02:00
function tryReuseStructureFromOldProgram ( ) : StructureIsReused {
2015-06-23 02:48:44 +02:00
if ( ! oldProgram ) {
2017-04-22 01:54:42 +02:00
return StructureIsReused . Not ;
2015-06-23 02:48:44 +02:00
}
2015-10-01 01:10:52 +02:00
2016-04-06 22:49:25 +02:00
// check properties that can affect structure of the program or module resolution strategy
// if any of these properties has changed - structure cannot be reused
const oldOptions = oldProgram . getCompilerOptions ( ) ;
2016-10-26 00:24:21 +02:00
if ( changesAffectModuleResolution ( oldOptions , options ) ) {
2017-04-22 01:54:42 +02:00
return oldProgram . structureIsReused = StructureIsReused . Not ;
2016-04-06 22:49:25 +02:00
}
2017-04-29 01:27:41 +02:00
Debug . assert ( ! ( oldProgram . structureIsReused & ( StructureIsReused . Completely | StructureIsReused . SafeModules ) ) ) ;
2015-06-23 02:48:44 +02:00
// there is an old program, check if we can reuse its structure
2015-11-04 23:02:33 +01:00
const oldRootNames = oldProgram . getRootFileNames ( ) ;
2015-06-25 02:40:04 +02:00
if ( ! arrayIsEqualTo ( oldRootNames , rootNames ) ) {
2017-05-02 00:16:42 +02:00
return oldProgram . structureIsReused = StructureIsReused . Not ;
2015-06-23 02:48:44 +02:00
}
2015-10-01 01:10:52 +02:00
2016-04-06 22:49:25 +02:00
if ( ! arrayIsEqualTo ( options . types , oldOptions . types ) ) {
2017-05-02 00:16:42 +02:00
return oldProgram . structureIsReused = StructureIsReused . Not ;
2016-04-06 22:49:25 +02:00
}
2015-06-23 02:48:44 +02:00
// check if program source files has changed in the way that can affect structure of the program
2015-11-04 23:02:33 +01:00
const newSourceFiles : SourceFile [ ] = [ ] ;
const filePaths : Path [ ] = [ ] ;
2016-11-02 22:41:26 +01:00
const modifiedSourceFiles : { oldFile : SourceFile , newFile : SourceFile } [ ] = [ ] ;
2017-04-22 01:54:42 +02:00
oldProgram . structureIsReused = StructureIsReused . Completely ;
2015-10-15 23:43:51 +02:00
2017-07-21 23:58:06 +02:00
// If the missing file paths are now present, it can change the progam structure,
// and hence cant reuse the structure.
// This is same as how we dont reuse the structure if one of the file from old program is now missing
if ( oldProgram . getMissingFilePaths ( ) . some ( missingFilePath = > host . fileExists ( missingFilePath ) ) ) {
return oldProgram . structureIsReused = StructureIsReused . Not ;
}
2017-08-09 23:39:06 +02:00
const oldSourceFiles = oldProgram . getSourceFiles ( ) ;
const enum SeenPackageName { Exists , Modified }
const seenPackageNames = createMap < SeenPackageName > ( ) ;
for ( const oldSourceFile of oldSourceFiles ) {
let newSourceFile = host . getSourceFileByPath
2017-07-25 01:57:49 +02:00
? host . getSourceFileByPath ( oldSourceFile . fileName , oldSourceFile . path , options . target , /*onError*/ undefined , shouldCreateNewSourceFile )
: host . getSourceFile ( oldSourceFile . fileName , options . target , /*onError*/ undefined , shouldCreateNewSourceFile ) ;
2016-05-11 08:43:26 +02:00
2015-06-25 02:40:04 +02:00
if ( ! newSourceFile ) {
2017-04-22 01:54:42 +02:00
return oldProgram . structureIsReused = StructureIsReused . Not ;
2015-06-25 02:40:04 +02:00
}
2017-08-09 23:39:06 +02:00
Debug . assert ( ! newSourceFile . redirectInfo , "Host should not return a redirect source file from `getSourceFile`" ) ;
let fileChanged : boolean ;
if ( oldSourceFile . redirectInfo ) {
// We got `newSourceFile` by path, so it is actually for the unredirected file.
// This lets us know if the unredirected file has changed. If it has we should break the redirect.
if ( newSourceFile !== oldSourceFile . redirectInfo . unredirected ) {
// Underlying file has changed. Might not redirect anymore. Must rebuild program.
return oldProgram . structureIsReused = StructureIsReused . Not ;
}
fileChanged = false ;
newSourceFile = oldSourceFile ; // Use the redirect.
}
else if ( oldProgram . redirectTargetsSet . has ( oldSourceFile . path ) ) {
// If a redirected-to source file changes, the redirect may be broken.
if ( newSourceFile !== oldSourceFile ) {
return oldProgram . structureIsReused = StructureIsReused . Not ;
}
fileChanged = false ;
}
else {
fileChanged = newSourceFile !== oldSourceFile ;
}
2015-10-29 22:54:56 +01:00
newSourceFile . path = oldSourceFile . path ;
filePaths . push ( newSourceFile . path ) ;
2015-10-15 23:43:51 +02:00
2017-08-09 23:39:06 +02:00
const packageName = oldProgram . sourceFileToPackageName . get ( oldSourceFile . path ) ;
if ( packageName !== undefined ) {
// If there are 2 different source files for the same package name and at least one of them changes,
// they might become redirects. So we must rebuild the program.
const prevKind = seenPackageNames . get ( packageName ) ;
const newKind = fileChanged ? SeenPackageName.Modified : SeenPackageName.Exists ;
if ( ( prevKind !== undefined && newKind === SeenPackageName . Modified ) || prevKind === SeenPackageName . Modified ) {
return oldProgram . structureIsReused = StructureIsReused . Not ;
}
seenPackageNames . set ( packageName , newKind ) ;
}
if ( fileChanged ) {
2017-04-29 01:27:41 +02:00
// The `newSourceFile` object was created for the new program.
2015-07-09 23:40:33 +02:00
if ( oldSourceFile . hasNoDefaultLib !== newSourceFile . hasNoDefaultLib ) {
// value of no-default-lib has changed
// this will affect if default library is injected into the list of files
2017-04-29 01:27:41 +02:00
oldProgram . structureIsReused = StructureIsReused . SafeModules ;
2015-07-09 23:40:33 +02:00
}
2015-06-23 02:48:44 +02:00
// check tripleslash references
if ( ! arrayIsEqualTo ( oldSourceFile . referencedFiles , newSourceFile . referencedFiles , fileReferenceIsEqualTo ) ) {
// tripleslash references has changed
2017-04-29 01:27:41 +02:00
oldProgram . structureIsReused = StructureIsReused . SafeModules ;
2015-06-23 02:48:44 +02:00
}
2015-10-01 01:10:52 +02:00
2015-12-22 22:21:51 +01:00
// check imports and module augmentations
2015-08-20 00:37:37 +02:00
collectExternalModuleReferences ( newSourceFile ) ;
2015-06-23 02:48:44 +02:00
if ( ! arrayIsEqualTo ( oldSourceFile . imports , newSourceFile . imports , module NameIsEqualTo ) ) {
// imports has changed
2017-04-29 01:27:41 +02:00
oldProgram . structureIsReused = StructureIsReused . SafeModules ;
2015-06-23 02:48:44 +02:00
}
2015-12-22 22:21:51 +01:00
if ( ! arrayIsEqualTo ( oldSourceFile . module Augmentations , newSourceFile . module Augmentations , module NameIsEqualTo ) ) {
// moduleAugmentations has changed
2017-04-29 01:27:41 +02:00
oldProgram . structureIsReused = StructureIsReused . SafeModules ;
2015-12-22 22:21:51 +01:00
}
2017-06-15 02:13:35 +02:00
if ( ( oldSourceFile . flags & NodeFlags . PossiblyContainsDynamicImport ) !== ( newSourceFile . flags & NodeFlags . PossiblyContainsDynamicImport ) ) {
// dynamicImport has changed
2017-06-15 06:50:12 +02:00
oldProgram . structureIsReused = StructureIsReused . SafeModules ;
2017-06-15 02:13:35 +02:00
}
2015-10-01 01:10:52 +02:00
2016-04-01 21:41:01 +02:00
if ( ! arrayIsEqualTo ( oldSourceFile . typeReferenceDirectives , newSourceFile . typeReferenceDirectives , fileReferenceIsEqualTo ) ) {
// 'types' references has changed
2017-04-29 01:27:41 +02:00
oldProgram . structureIsReused = StructureIsReused . SafeModules ;
2016-04-01 21:41:01 +02:00
}
2016-11-02 22:41:26 +01:00
// tentatively approve the file
modifiedSourceFiles . push ( { oldFile : oldSourceFile , newFile : newSourceFile } ) ;
2015-06-23 02:48:44 +02:00
}
2017-08-05 14:01:33 +02:00
else if ( hasInvalidatedResolution ( oldSourceFile . path ) ) {
// 'module/types' references could have changed
oldProgram . structureIsReused = StructureIsReused . SafeModules ;
// add file to the modified list so that we will resolve it later
modifiedSourceFiles . push ( { oldFile : oldSourceFile , newFile : newSourceFile } ) ;
}
2015-10-01 01:10:52 +02:00
2015-06-23 02:48:44 +02:00
// if file has passed all checks it should be safe to reuse it
newSourceFiles . push ( newSourceFile ) ;
}
2015-10-01 01:10:52 +02:00
2017-04-22 01:54:42 +02:00
if ( oldProgram . structureIsReused !== StructureIsReused . Completely ) {
return oldProgram . structureIsReused ;
2015-06-23 02:48:44 +02:00
}
2015-10-01 01:10:52 +02:00
2017-04-19 00:48:01 +02:00
modifiedFilePaths = modifiedSourceFiles . map ( f = > f . newFile . path ) ;
2016-11-09 16:41:25 +01:00
// try to verify results of module resolution
2016-11-02 22:41:26 +01:00
for ( const { oldFile : oldSourceFile , newFile : newSourceFile } of modifiedSourceFiles ) {
const newSourceFilePath = getNormalizedAbsolutePath ( newSourceFile . fileName , currentDirectory ) ;
if ( resolveModuleNamesWorker ) {
2017-08-26 00:14:51 +02:00
const module Names = getModuleNames ( newSourceFile ) ;
2017-12-06 22:17:21 +01:00
const oldProgramState : OldProgramState = { program : oldProgram , oldSourceFile , modifiedFilePaths } ;
2017-04-19 00:48:01 +02:00
const resolutions = resolveModuleNamesReusingOldState ( module Names , newSourceFilePath , newSourceFile , oldProgramState ) ;
2016-11-02 22:41:26 +01:00
// ensure that module resolution results are still correct
const resolutionsChanged = hasChangesInResolutions ( module Names , resolutions , oldSourceFile . resolvedModules , module ResolutionIsEqualTo ) ;
if ( resolutionsChanged ) {
2017-04-29 01:27:41 +02:00
oldProgram . structureIsReused = StructureIsReused . SafeModules ;
newSourceFile . resolvedModules = zipToMap ( module Names , resolutions ) ;
}
else {
newSourceFile . resolvedModules = oldSourceFile . resolvedModules ;
2016-11-02 22:41:26 +01:00
}
}
if ( resolveTypeReferenceDirectiveNamesWorker ) {
const typesReferenceDirectives = map ( newSourceFile . typeReferenceDirectives , x = > x . fileName ) ;
const resolutions = resolveTypeReferenceDirectiveNamesWorker ( typesReferenceDirectives , newSourceFilePath ) ;
// ensure that types resolutions are still correct
const resolutionsChanged = hasChangesInResolutions ( typesReferenceDirectives , resolutions , oldSourceFile . resolvedTypeReferenceDirectiveNames , typeDirectiveIsEqualTo ) ;
if ( resolutionsChanged ) {
2017-04-29 01:27:41 +02:00
oldProgram . structureIsReused = StructureIsReused . SafeModules ;
newSourceFile . resolvedTypeReferenceDirectiveNames = zipToMap ( typesReferenceDirectives , resolutions ) ;
}
else {
newSourceFile . resolvedTypeReferenceDirectiveNames = oldSourceFile . resolvedTypeReferenceDirectiveNames ;
2016-11-02 22:41:26 +01:00
}
}
}
2017-04-28 05:27:48 +02:00
if ( oldProgram . structureIsReused !== StructureIsReused . Completely ) {
return oldProgram . structureIsReused ;
2016-11-02 22:41:26 +01:00
}
2017-09-26 22:34:56 +02:00
if ( host . hasChangedAutomaticTypeDirectiveNames ) {
2017-06-22 22:47:54 +02:00
return oldProgram . structureIsReused = StructureIsReused . SafeModules ;
}
2017-06-21 01:31:47 +02:00
2017-07-21 23:58:06 +02:00
missingFilePaths = oldProgram . getMissingFilePaths ( ) ;
2017-06-21 00:54:43 +02:00
2015-06-23 02:48:44 +02:00
// update fileName -> file mapping
2016-12-19 19:12:35 +01:00
for ( let i = 0 ; i < newSourceFiles . length ; i ++ ) {
2015-10-30 00:43:12 +01:00
filesByName . set ( filePaths [ i ] , newSourceFiles [ i ] ) ;
2017-10-24 04:05:38 +02:00
// Set the file as found during node modules search if it was found that way in old progra,
if ( oldProgram . isSourceFileFromExternalLibrary ( oldProgram . getSourceFileByPath ( filePaths [ i ] ) ) ) {
sourceFilesFoundSearchingNodeModules . set ( filePaths [ i ] , true ) ;
}
2015-06-23 02:48:44 +02:00
}
2015-10-01 01:10:52 +02:00
2015-06-23 02:48:44 +02:00
files = newSourceFiles ;
2015-09-10 19:46:39 +02:00
fileProcessingDiagnostics = oldProgram . getFileProcessingDiagnostics ( ) ;
2015-10-01 01:10:52 +02:00
2015-11-04 23:02:33 +01:00
for ( const modifiedFile of modifiedSourceFiles ) {
2016-11-02 22:41:26 +01:00
fileProcessingDiagnostics . reattachFileDiagnostics ( modifiedFile . newFile ) ;
2015-09-10 19:46:39 +02:00
}
2016-04-12 06:36:07 +02:00
resolvedTypeReferenceDirectives = oldProgram . getResolvedTypeReferenceDirectives ( ) ;
2015-10-01 01:10:52 +02:00
2017-08-09 23:39:06 +02:00
sourceFileToPackageName = oldProgram . sourceFileToPackageName ;
redirectTargetsSet = oldProgram . redirectTargetsSet ;
2017-04-22 01:54:42 +02:00
return oldProgram . structureIsReused = StructureIsReused . Completely ;
2015-06-23 02:48:44 +02:00
}
2015-02-05 10:47:29 +01:00
function getEmitHost ( writeFileCallback? : WriteFileCallback ) : EmitHost {
2015-02-05 01:53:14 +01:00
return {
2015-10-15 23:43:51 +02:00
getCanonicalFileName ,
2015-02-05 01:53:14 +01:00
getCommonSourceDirectory : program.getCommonSourceDirectory ,
getCompilerOptions : program.getCompilerOptions ,
2015-10-15 23:43:51 +02:00
getCurrentDirectory : ( ) = > currentDirectory ,
2015-03-24 18:05:24 +01:00
getNewLine : ( ) = > host . getNewLine ( ) ,
2015-02-05 01:53:14 +01:00
getSourceFile : program.getSourceFile ,
2016-05-11 08:43:26 +02:00
getSourceFileByPath : program.getSourceFileByPath ,
2015-02-05 01:53:14 +01:00
getSourceFiles : program.getSourceFiles ,
2016-11-02 15:05:54 +01:00
isSourceFileFromExternalLibrary ,
2015-03-24 18:05:24 +01:00
writeFile : writeFileCallback || (
2016-03-15 21:25:18 +01:00
( fileName , data , writeByteOrderMark , onError , sourceFiles ) = > host . writeFile ( fileName , data , writeByteOrderMark , onError , sourceFiles ) ) ,
2015-10-20 20:22:46 +02:00
isEmitBlocked ,
2015-02-05 01:53:14 +01:00
} ;
2014-12-16 23:42:58 +01:00
}
2016-11-08 06:13:11 +01:00
function isSourceFileFromExternalLibrary ( file : SourceFile ) : boolean {
2016-12-05 23:13:32 +01:00
return sourceFilesFoundSearchingNodeModules . get ( file . path ) ;
2016-11-08 06:13:11 +01:00
}
2017-07-25 23:21:57 +02:00
function isSourceFileDefaultLibrary ( file : SourceFile ) : boolean {
2017-08-15 03:19:43 +02:00
if ( file . hasNoDefaultLib ) {
return true ;
}
2017-09-27 02:29:53 +02:00
if ( ! options . noLib ) {
return false ;
2017-08-15 03:19:43 +02:00
}
2017-09-27 02:29:53 +02:00
// If '--lib' is not specified, include default library file according to '--target'
// otherwise, using options specified in '--lib' instead of '--target' default library file
2017-10-26 20:56:29 +02:00
const equalityComparer = host . useCaseSensitiveFileNames ( ) ? equateStringsCaseSensitive : equateStringsCaseInsensitive ;
2017-09-27 02:29:53 +02:00
if ( ! options . lib ) {
2017-10-26 02:46:39 +02:00
return equalityComparer ( file . fileName , getDefaultLibraryFileName ( ) ) ;
2017-09-27 02:29:53 +02:00
}
else {
2017-10-26 02:46:39 +02:00
return forEach ( options . lib , libFileName = > equalityComparer ( file . fileName , combinePaths ( defaultLibraryPath , libFileName ) ) ) ;
2017-09-27 02:29:53 +02:00
}
2017-07-25 23:21:57 +02:00
}
2014-12-16 23:12:17 +01:00
function getDiagnosticsProducingTypeChecker() {
return diagnosticsProducingTypeChecker || ( diagnosticsProducingTypeChecker = createTypeChecker ( program , /*produceDiagnostics:*/ true ) ) ;
}
2016-06-18 01:22:03 +02:00
function dropDiagnosticsProducingTypeChecker() {
diagnosticsProducingTypeChecker = undefined ;
}
2015-02-05 01:11:38 +01:00
function getTypeChecker() {
return noDiagnosticsTypeChecker || ( noDiagnosticsTypeChecker = createTypeChecker ( program , /*produceDiagnostics:*/ false ) ) ;
2014-12-16 23:12:17 +01:00
}
2017-02-07 23:36:15 +01:00
function emit ( sourceFile? : SourceFile , writeFileCallback? : WriteFileCallback , cancellationToken? : CancellationToken , emitOnlyDtsFiles? : boolean , transformers? : CustomTransformers ) : EmitResult {
return runWithCancellationToken ( ( ) = > emitWorker ( program , sourceFile , writeFileCallback , cancellationToken , emitOnlyDtsFiles , transformers ) ) ;
2015-06-18 19:52:19 +02:00
}
2015-10-20 20:22:46 +02:00
function isEmitBlocked ( emitFileName : string ) : boolean {
2017-07-10 20:24:17 +02:00
return hasEmitBlockingDiagnostics . has ( toPath ( emitFileName ) ) ;
2015-10-20 20:22:46 +02:00
}
2017-02-07 23:36:15 +01:00
function emitWorker ( program : Program , sourceFile : SourceFile , writeFileCallback : WriteFileCallback , cancellationToken : CancellationToken , emitOnlyDtsFiles? : boolean , customTransformers? : CustomTransformers ) : EmitResult {
2017-08-24 18:55:01 +02:00
let declarationDiagnostics : ReadonlyArray < Diagnostic > = [ ] ;
2016-02-17 07:01:28 +01:00
2017-11-23 03:34:49 +01:00
if ( ! emitOnlyDtsFiles ) {
if ( options . noEmit ) {
return { diagnostics : declarationDiagnostics , sourceMaps : undefined , emittedFiles : undefined , emitSkipped : true } ;
2016-02-17 07:01:28 +01:00
}
2017-11-23 03:34:49 +01:00
// If the noEmitOnError flag is set, then check if we have any errors so far. If so,
// immediately bail out. Note that we pass 'undefined' for 'sourceFile' so that we
// get any preEmit diagnostics, not just the ones
if ( options . noEmitOnError ) {
const diagnostics = [
. . . program . getOptionsDiagnostics ( cancellationToken ) ,
. . . program . getSyntacticDiagnostics ( sourceFile , cancellationToken ) ,
. . . program . getGlobalDiagnostics ( cancellationToken ) ,
. . . program . getSemanticDiagnostics ( sourceFile , cancellationToken )
] ;
if ( diagnostics . length === 0 && program . getCompilerOptions ( ) . declaration ) {
declarationDiagnostics = program . getDeclarationDiagnostics ( /*sourceFile*/ undefined , cancellationToken ) ;
}
if ( diagnostics . length > 0 || declarationDiagnostics . length > 0 ) {
return {
diagnostics : concatenate ( diagnostics , declarationDiagnostics ) ,
sourceMaps : undefined ,
emittedFiles : undefined ,
emitSkipped : true
} ;
}
2015-12-22 06:43:51 +01:00
}
2014-12-16 22:52:47 +01:00
}
2015-02-05 23:41:04 +01:00
2015-02-26 01:45:45 +01:00
// Create the emit resolver outside of the "emitTime" tracking code below. That way
// any cost associated with it (like type checking) are appropriate associated with
// the type-checking counter.
2015-05-08 22:58:20 +02:00
//
// If the -out option is specified, we should not pass the source file to getEmitResolver.
// This is because in the -out scenario all files need to be emitted, and therefore all
// files need to be type checked. And the way to specify that all files need to be type
// checked is to not pass the file to getEmitResolver.
2018-02-01 01:22:27 +01:00
const emitResolver = getDiagnosticsProducingTypeChecker ( ) . getEmitResolver ( ( options . outFile || options . out ) ? undefined : sourceFile , cancellationToken ) ;
2015-02-26 01:45:45 +01:00
2016-08-15 20:07:49 +02:00
performance . mark ( "beforeEmit" ) ;
2015-02-05 03:42:44 +01:00
2017-02-07 23:36:15 +01:00
const transformers = emitOnlyDtsFiles ? [ ] : getTransformers ( options , customTransformers ) ;
2015-11-04 23:02:33 +01:00
const emitResult = emitFiles (
2015-02-26 01:45:45 +01:00
emitResolver ,
2015-02-05 23:41:04 +01:00
getEmitHost ( writeFileCallback ) ,
2016-08-24 01:11:52 +02:00
sourceFile ,
2017-02-07 23:36:15 +01:00
emitOnlyDtsFiles ,
transformers ) ;
2015-02-05 03:42:44 +01:00
2016-08-15 20:07:49 +02:00
performance . mark ( "afterEmit" ) ;
performance . measure ( "Emit" , "beforeEmit" , "afterEmit" ) ;
2015-02-06 00:50:18 +01:00
return emitResult ;
2015-01-15 22:22:23 +01:00
}
2015-02-06 00:50:18 +01:00
2017-06-03 00:10:41 +02:00
function getSourceFile ( fileName : string ) : SourceFile | undefined {
2017-07-10 20:24:17 +02:00
return getSourceFileByPath ( toPath ( fileName ) ) ;
2016-05-11 08:43:26 +02:00
}
2017-06-03 00:10:41 +02:00
function getSourceFileByPath ( path : Path ) : SourceFile | undefined {
2016-05-11 08:43:26 +02:00
return filesByName . get ( path ) ;
2014-12-16 22:14:14 +01:00
}
2015-06-18 19:52:19 +02:00
function getDiagnosticsHelper (
2016-02-23 21:48:31 +01:00
sourceFile : SourceFile ,
2017-08-24 18:55:01 +02:00
getDiagnostics : ( sourceFile : SourceFile , cancellationToken : CancellationToken ) = > ReadonlyArray < Diagnostic > ,
cancellationToken : CancellationToken ) : ReadonlyArray < Diagnostic > {
2015-02-05 10:47:29 +01:00
if ( sourceFile ) {
2015-06-18 19:52:19 +02:00
return getDiagnostics ( sourceFile , cancellationToken ) ;
2014-12-16 22:52:47 +01:00
}
2017-06-05 23:11:43 +02:00
return sortAndDeduplicateDiagnostics ( flatMap ( program . getSourceFiles ( ) , sourceFile = > {
2015-06-18 19:52:19 +02:00
if ( cancellationToken ) {
cancellationToken . throwIfCancellationRequested ( ) ;
}
2017-06-05 23:11:43 +02:00
return getDiagnostics ( sourceFile , cancellationToken ) ;
} ) ) ;
2014-12-16 22:52:47 +01:00
}
2017-08-24 18:55:01 +02:00
function getSyntacticDiagnostics ( sourceFile : SourceFile , cancellationToken : CancellationToken ) : ReadonlyArray < Diagnostic > {
2015-06-18 19:52:19 +02:00
return getDiagnosticsHelper ( sourceFile , getSyntacticDiagnosticsForFile , cancellationToken ) ;
2014-12-16 22:14:14 +01:00
}
2017-08-24 18:55:01 +02:00
function getSemanticDiagnostics ( sourceFile : SourceFile , cancellationToken : CancellationToken ) : ReadonlyArray < Diagnostic > {
2015-06-18 19:52:19 +02:00
return getDiagnosticsHelper ( sourceFile , getSemanticDiagnosticsForFile , cancellationToken ) ;
2015-01-15 22:22:23 +01:00
}
2015-02-05 11:15:38 +01:00
2017-08-24 18:55:01 +02:00
function getDeclarationDiagnostics ( sourceFile : SourceFile , cancellationToken : CancellationToken ) : ReadonlyArray < Diagnostic > {
2016-02-24 23:21:30 +01:00
const options = program . getCompilerOptions ( ) ;
2016-02-24 23:30:21 +01:00
// collect diagnostics from the program only once if either no source file was specified or out/outFile is set (bundled emit)
2016-02-24 23:21:30 +01:00
if ( ! sourceFile || options . out || options . outFile ) {
return getDeclarationDiagnosticsWorker ( sourceFile , cancellationToken ) ;
}
else {
return getDiagnosticsHelper ( sourceFile , getDeclarationDiagnosticsForFile , cancellationToken ) ;
}
2015-03-20 00:55:07 +01:00
}
2017-08-24 18:55:01 +02:00
function getSyntacticDiagnosticsForFile ( sourceFile : SourceFile ) : ReadonlyArray < Diagnostic > {
2016-10-25 01:57:43 +02:00
// For JavaScript files, we report semantic errors for using TypeScript-only
// constructs from within a JavaScript file as syntactic errors.
2016-10-28 01:38:59 +02:00
if ( isSourceFileJavaScript ( sourceFile ) ) {
if ( ! sourceFile . additionalSyntacticDiagnostics ) {
sourceFile . additionalSyntacticDiagnostics = getJavaScriptSyntacticDiagnosticsForFile ( sourceFile ) ;
}
return concatenate ( sourceFile . additionalSyntacticDiagnostics , sourceFile . parseDiagnostics ) ;
2016-10-25 01:57:43 +02:00
}
2015-02-05 22:38:11 +01:00
return sourceFile . parseDiagnostics ;
2014-12-16 22:14:14 +01:00
}
2015-06-18 19:52:19 +02:00
function runWithCancellationToken < T > ( func : ( ) = > T ) : T {
try {
return func ( ) ;
}
catch ( e ) {
if ( e instanceof OperationCanceledException ) {
2015-07-09 00:35:49 +02:00
// We were canceled while performing the operation. Because our type checker
2015-06-18 19:52:19 +02:00
// might be a bad state, we need to throw it away.
2015-07-07 00:31:22 +02:00
//
2016-02-11 17:56:45 +01:00
// Note: we are overly aggressive here. We do not actually *have* to throw away
2015-07-07 00:31:22 +02:00
// the "noDiagnosticsTypeChecker". However, for simplicity, i'd like to keep
// the lifetimes of these two TypeCheckers the same. Also, we generally only
// cancel when the user has made a change anyways. And, in that case, we (the
2015-07-09 00:35:49 +02:00
// program instance) will get thrown away anyways. So trying to keep one of
2015-07-07 00:31:22 +02:00
// these type checkers alive doesn't serve much purpose.
2015-06-18 19:52:19 +02:00
noDiagnosticsTypeChecker = undefined ;
diagnosticsProducingTypeChecker = undefined ;
}
2015-02-05 10:47:29 +01:00
2015-06-18 19:52:19 +02:00
throw e ;
}
}
2015-02-05 10:47:29 +01:00
2015-06-18 21:04:26 +02:00
function getSemanticDiagnosticsForFile ( sourceFile : SourceFile , cancellationToken : CancellationToken ) : Diagnostic [ ] {
2017-03-07 22:26:41 +01:00
return getAndCacheDiagnostics ( sourceFile , cancellationToken , cachedSemanticDiagnosticsForFile , getSemanticDiagnosticsForFileNoCache ) ;
}
function getSemanticDiagnosticsForFileNoCache ( sourceFile : SourceFile , cancellationToken : CancellationToken ) : Diagnostic [ ] {
2015-06-18 19:52:19 +02:00
return runWithCancellationToken ( ( ) = > {
2017-04-19 01:19:58 +02:00
// If skipLibCheck is enabled, skip reporting errors if file is a declaration file.
// If skipDefaultLibCheck is enabled, skip reporting errors if file contains a
// '/// <reference no-default-lib="true"/>' directive.
if ( options . skipLibCheck && sourceFile . isDeclarationFile || options . skipDefaultLibCheck && sourceFile . hasNoDefaultLib ) {
return emptyArray ;
}
2015-11-04 23:02:33 +01:00
const typeChecker = getDiagnosticsProducingTypeChecker ( ) ;
2015-02-05 10:47:29 +01:00
2015-06-18 19:52:19 +02:00
Debug . assert ( ! ! sourceFile . bindDiagnostics ) ;
2017-10-16 22:38:28 +02:00
2017-11-20 17:34:01 +01:00
const isCheckJs = isCheckJsEnabledForFile ( sourceFile , options ) ;
2017-10-16 22:38:28 +02:00
// By default, only type-check .ts, .tsx, and 'External' files (external files are added by plugins)
const includeBindAndCheckDiagnostics = sourceFile . scriptKind === ScriptKind . TS || sourceFile . scriptKind === ScriptKind . TSX ||
2017-11-20 17:34:01 +01:00
sourceFile . scriptKind === ScriptKind . External || isCheckJs ;
2017-06-02 04:05:07 +02:00
const bindDiagnostics = includeBindAndCheckDiagnostics ? sourceFile.bindDiagnostics : emptyArray ;
const checkDiagnostics = includeBindAndCheckDiagnostics ? typeChecker . getDiagnostics ( sourceFile , cancellationToken ) : emptyArray ;
2015-11-04 23:02:33 +01:00
const fileProcessingDiagnosticsInFile = fileProcessingDiagnostics . getDiagnostics ( sourceFile . fileName ) ;
const programDiagnosticsInFile = programDiagnostics . getDiagnostics ( sourceFile . fileName ) ;
2017-11-17 19:38:30 +01:00
let diagnostics = bindDiagnostics . concat ( checkDiagnostics , fileProcessingDiagnosticsInFile , programDiagnosticsInFile ) ;
2017-11-20 17:34:01 +01:00
if ( isCheckJs ) {
2017-11-17 19:38:30 +01:00
diagnostics = concatenate ( diagnostics , sourceFile . jsDocDiagnostics ) ;
}
2017-09-14 00:40:10 +02:00
return filter ( diagnostics , shouldReportDiagnostic ) ;
2015-06-18 19:52:19 +02:00
} ) ;
2014-12-16 22:14:14 +01:00
}
2017-03-08 08:03:47 +01:00
/ * *
2017-03-23 00:23:21 +01:00
* Skip errors if previous line start with '// @ts-ignore' comment , not counting non - empty non - comment lines
2017-03-08 08:03:47 +01:00
* /
function shouldReportDiagnostic ( diagnostic : Diagnostic ) {
const { file , start } = diagnostic ;
2017-04-17 22:15:06 +02:00
if ( file ) {
const lineStarts = getLineStarts ( file ) ;
let { line } = computeLineAndCharacterOfPosition ( lineStarts , start ) ;
while ( line > 0 ) {
const previousLineText = file . text . slice ( lineStarts [ line - 1 ] , lineStarts [ line ] ) ;
const result = ignoreDiagnosticCommentRegEx . exec ( previousLineText ) ;
if ( ! result ) {
// non-empty line
return true ;
}
if ( result [ 3 ] ) {
// @ts-ignore
return false ;
}
line -- ;
2017-03-08 08:03:47 +01:00
}
}
return true ;
}
2016-10-28 01:38:59 +02:00
function getJavaScriptSyntacticDiagnosticsForFile ( sourceFile : SourceFile ) : Diagnostic [ ] {
2015-09-09 23:41:19 +02:00
return runWithCancellationToken ( ( ) = > {
2015-11-06 21:39:42 +01:00
const diagnostics : Diagnostic [ ] = [ ] ;
2016-11-01 20:54:27 +01:00
let parent : Node = sourceFile ;
2015-09-09 23:41:19 +02:00
walk ( sourceFile ) ;
return diagnostics ;
2016-11-01 20:54:27 +01:00
function walk ( node : Node ) {
// Return directly from the case if the given node doesnt want to visit each child
// Otherwise break to visit each child
switch ( parent . kind ) {
case SyntaxKind . Parameter :
case SyntaxKind . PropertyDeclaration :
if ( ( < ParameterDeclaration | PropertyDeclaration > parent ) . questionToken === node ) {
diagnostics . push ( createDiagnosticForNode ( node , Diagnostics . _0_can_only_be_used_in_a_ts_file , "?" ) ) ;
return ;
}
2017-08-15 03:19:43 +02:00
// falls through
2016-11-01 20:54:27 +01:00
case SyntaxKind . MethodDeclaration :
case SyntaxKind . MethodSignature :
case SyntaxKind . Constructor :
case SyntaxKind . GetAccessor :
case SyntaxKind . SetAccessor :
case SyntaxKind . FunctionExpression :
case SyntaxKind . FunctionDeclaration :
case SyntaxKind . ArrowFunction :
case SyntaxKind . VariableDeclaration :
// type annotation
if ( ( < FunctionLikeDeclaration | VariableDeclaration | ParameterDeclaration | PropertyDeclaration > parent ) . type === node ) {
diagnostics . push ( createDiagnosticForNode ( node , Diagnostics . types_can_only_be_used_in_a_ts_file ) ) ;
return ;
}
2015-09-09 23:41:19 +02:00
}
switch ( node . kind ) {
case SyntaxKind . ImportEqualsDeclaration :
diagnostics . push ( createDiagnosticForNode ( node , Diagnostics . import_can_only_be_used_in_a_ts_file ) ) ;
2016-11-01 20:54:27 +01:00
return ;
2015-09-09 23:41:19 +02:00
case SyntaxKind . ExportAssignment :
2016-02-01 22:28:58 +01:00
if ( ( < ExportAssignment > node ) . isExportEquals ) {
diagnostics . push ( createDiagnosticForNode ( node , Diagnostics . export_can_only_be_used_in_a_ts_file ) ) ;
2016-11-01 20:54:27 +01:00
return ;
2015-09-09 23:41:19 +02:00
}
break ;
case SyntaxKind . HeritageClause :
2016-12-18 07:44:54 +01:00
const heritageClause = < HeritageClause > node ;
2015-09-09 23:41:19 +02:00
if ( heritageClause . token === SyntaxKind . ImplementsKeyword ) {
diagnostics . push ( createDiagnosticForNode ( node , Diagnostics . implements_clauses_can_only_be_used_in_a_ts_file ) ) ;
2016-11-01 20:54:27 +01:00
return ;
2015-09-09 23:41:19 +02:00
}
break ;
case SyntaxKind . InterfaceDeclaration :
diagnostics . push ( createDiagnosticForNode ( node , Diagnostics . interface_declarations_can_only_be_used_in_a_ts_file ) ) ;
2016-11-01 20:54:27 +01:00
return ;
2015-09-09 23:41:19 +02:00
case SyntaxKind . ModuleDeclaration :
diagnostics . push ( createDiagnosticForNode ( node , Diagnostics . module _declarations_can_only_be_used_in_a_ts_file ) ) ;
2016-11-01 20:54:27 +01:00
return ;
2015-09-09 23:41:19 +02:00
case SyntaxKind . TypeAliasDeclaration :
diagnostics . push ( createDiagnosticForNode ( node , Diagnostics . type_aliases_can_only_be_used_in_a_ts_file ) ) ;
2016-11-01 20:54:27 +01:00
return ;
case SyntaxKind . EnumDeclaration :
diagnostics . push ( createDiagnosticForNode ( node , Diagnostics . enum_declarations_can_only_be_used_in_a_ts_file ) ) ;
return ;
2017-07-29 14:41:08 +02:00
case SyntaxKind . NonNullExpression :
diagnostics . push ( createDiagnosticForNode ( node , Diagnostics . non_null_assertions_can_only_be_used_in_a_ts_file ) ) ;
return ;
case SyntaxKind . AsExpression :
diagnostics . push ( createDiagnosticForNode ( ( node as AsExpression ) . type , Diagnostics . type_assertion_expressions_can_only_be_used_in_a_ts_file ) ) ;
2016-11-01 20:54:27 +01:00
return ;
2017-07-29 14:41:08 +02:00
case SyntaxKind . TypeAssertionExpression :
Debug . fail ( ) ; // Won't parse these in a JS file anyway, as they are interpreted as JSX.
2016-11-01 20:54:27 +01:00
}
const prevParent = parent ;
parent = node ;
forEachChild ( node , walk , walkArray ) ;
parent = prevParent ;
}
function walkArray ( nodes : NodeArray < Node > ) {
if ( parent . decorators === nodes && ! options . experimentalDecorators ) {
diagnostics . push ( createDiagnosticForNode ( parent , Diagnostics . Experimental_support_for_decorators_is_a_feature_that_is_subject_to_change_in_a_future_release_Set_the_experimentalDecorators_option_to_remove_this_warning ) ) ;
}
switch ( parent . kind ) {
case SyntaxKind . ClassDeclaration :
2015-09-09 23:41:19 +02:00
case SyntaxKind . MethodDeclaration :
case SyntaxKind . MethodSignature :
case SyntaxKind . Constructor :
case SyntaxKind . GetAccessor :
case SyntaxKind . SetAccessor :
case SyntaxKind . FunctionExpression :
case SyntaxKind . FunctionDeclaration :
case SyntaxKind . ArrowFunction :
2016-11-01 20:54:27 +01:00
// Check type parameters
if ( nodes === ( < ClassDeclaration | FunctionLikeDeclaration > parent ) . typeParameters ) {
diagnostics . push ( createDiagnosticForNodeArray ( nodes , Diagnostics . type_parameter_declarations_can_only_be_used_in_a_ts_file ) ) ;
return ;
2015-09-09 23:41:19 +02:00
}
2017-08-15 03:19:43 +02:00
// falls through
2015-09-09 23:41:19 +02:00
case SyntaxKind . VariableStatement :
2016-11-01 20:54:27 +01:00
// Check modifiers
if ( nodes === ( < ClassDeclaration | FunctionLikeDeclaration | VariableStatement > parent ) . modifiers ) {
2016-11-10 17:53:32 +01:00
return checkModifiers ( < NodeArray < Modifier > > nodes , parent . kind === SyntaxKind . VariableStatement ) ;
2015-09-09 23:41:19 +02:00
}
break ;
case SyntaxKind . PropertyDeclaration :
2016-11-01 20:54:27 +01:00
// Check modifiers of property declaration
if ( nodes === ( < PropertyDeclaration > parent ) . modifiers ) {
for ( const modifier of < NodeArray < Modifier > > nodes ) {
2016-06-15 18:42:52 +02:00
if ( modifier . kind !== SyntaxKind . StaticKeyword ) {
diagnostics . push ( createDiagnosticForNode ( modifier , Diagnostics . _0_can_only_be_used_in_a_ts_file , tokenToString ( modifier . kind ) ) ) ;
}
}
2016-11-01 20:54:27 +01:00
return ;
2016-06-15 18:42:52 +02:00
}
2016-11-01 20:54:27 +01:00
break ;
case SyntaxKind . Parameter :
// Check modifiers of parameter declaration
if ( nodes === ( < ParameterDeclaration > parent ) . modifiers ) {
diagnostics . push ( createDiagnosticForNodeArray ( nodes , Diagnostics . parameter_modifiers_can_only_be_used_in_a_ts_file ) ) ;
return ;
2016-06-15 18:42:52 +02:00
}
break ;
2016-11-01 20:54:27 +01:00
case SyntaxKind . CallExpression :
case SyntaxKind . NewExpression :
case SyntaxKind . ExpressionWithTypeArguments :
// Check type arguments
if ( nodes === ( < CallExpression | NewExpression | ExpressionWithTypeArguments > parent ) . typeArguments ) {
diagnostics . push ( createDiagnosticForNodeArray ( nodes , Diagnostics . type_arguments_can_only_be_used_in_a_ts_file ) ) ;
return ;
2016-02-13 04:19:23 +01:00
}
2016-11-01 20:54:27 +01:00
break ;
2015-09-09 23:41:19 +02:00
}
2016-11-01 20:54:27 +01:00
for ( const node of nodes ) {
walk ( node ) ;
2015-09-09 23:41:19 +02:00
}
}
2016-11-09 01:28:10 +01:00
function checkModifiers ( modifiers : NodeArray < Modifier > , isConstValid : boolean ) {
2016-11-01 20:54:27 +01:00
for ( const modifier of modifiers ) {
switch ( modifier . kind ) {
2016-11-09 01:28:10 +01:00
case SyntaxKind . ConstKeyword :
if ( isConstValid ) {
continue ;
}
2017-08-15 03:19:43 +02:00
// to report error,
// falls through
2016-11-01 20:54:27 +01:00
case SyntaxKind . PublicKeyword :
case SyntaxKind . PrivateKeyword :
case SyntaxKind . ProtectedKeyword :
case SyntaxKind . ReadonlyKeyword :
case SyntaxKind . DeclareKeyword :
2016-11-09 01:28:10 +01:00
case SyntaxKind . AbstractKeyword :
2016-11-01 20:54:27 +01:00
diagnostics . push ( createDiagnosticForNode ( modifier , Diagnostics . _0_can_only_be_used_in_a_ts_file , tokenToString ( modifier . kind ) ) ) ;
break ;
// These are all legal modifiers.
case SyntaxKind . StaticKeyword :
case SyntaxKind . ExportKeyword :
case SyntaxKind . DefaultKeyword :
2015-09-09 23:41:19 +02:00
}
}
}
2016-11-01 20:54:27 +01:00
function createDiagnosticForNodeArray ( nodes : NodeArray < Node > , message : DiagnosticMessage , arg0? : string | number , arg1? : string | number , arg2? : string | number ) : Diagnostic {
const start = nodes . pos ;
return createFileDiagnostic ( sourceFile , start , nodes . end - start , message , arg0 , arg1 , arg2 ) ;
2015-09-09 23:41:19 +02:00
}
2016-11-01 19:05:09 +01:00
// Since these are syntactic diagnostics, parent might not have been set
// this means the sourceFile cannot be infered from the node
function createDiagnosticForNode ( node : Node , message : DiagnosticMessage , arg0? : string | number , arg1? : string | number , arg2? : string | number ) : Diagnostic {
return createDiagnosticForNodeInSourceFile ( sourceFile , node , message , arg0 , arg1 , arg2 ) ;
2015-09-09 23:41:19 +02:00
}
} ) ;
}
2017-03-07 22:26:41 +01:00
function getDeclarationDiagnosticsWorker ( sourceFile : SourceFile | undefined , cancellationToken : CancellationToken ) : Diagnostic [ ] {
return getAndCacheDiagnostics ( sourceFile , cancellationToken , cachedDeclarationDiagnosticsForFile , getDeclarationDiagnosticsForFileNoCache ) ;
}
2017-03-17 21:37:12 +01:00
function getDeclarationDiagnosticsForFileNoCache ( sourceFile : SourceFile | undefined , cancellationToken : CancellationToken ) {
2015-06-18 19:52:19 +02:00
return runWithCancellationToken ( ( ) = > {
2016-02-24 23:21:30 +01:00
const resolver = getDiagnosticsProducingTypeChecker ( ) . getEmitResolver ( sourceFile , cancellationToken ) ;
// Don't actually write any files since we're just getting diagnostics.
2016-10-19 23:15:14 +02:00
return ts . getDeclarationDiagnostics ( getEmitHost ( noop ) , resolver , sourceFile ) ;
2015-06-18 19:52:19 +02:00
} ) ;
2015-03-20 00:55:07 +01:00
}
2017-03-07 22:26:41 +01:00
function getAndCacheDiagnostics (
sourceFile : SourceFile | undefined ,
cancellationToken : CancellationToken ,
cache : DiagnosticCache ,
getDiagnostics : ( sourceFile : SourceFile , cancellationToken : CancellationToken ) = > Diagnostic [ ] ) {
const cachedResult = sourceFile
? cache . perFile && cache . perFile . get ( sourceFile . path )
: cache . allDiagnostics ;
if ( cachedResult ) {
return cachedResult ;
}
const result = getDiagnostics ( sourceFile , cancellationToken ) || emptyArray ;
if ( sourceFile ) {
if ( ! cache . perFile ) {
2017-06-28 22:15:34 +02:00
cache . perFile = createMap < Diagnostic [ ] > ( ) ;
2017-03-07 22:26:41 +01:00
}
cache . perFile . set ( sourceFile . path , result ) ;
}
else {
cache . allDiagnostics = result ;
}
return result ;
}
2016-02-24 23:21:30 +01:00
function getDeclarationDiagnosticsForFile ( sourceFile : SourceFile , cancellationToken : CancellationToken ) : Diagnostic [ ] {
2017-05-17 17:12:23 +02:00
return sourceFile . isDeclarationFile ? [ ] : getDeclarationDiagnosticsWorker ( sourceFile , cancellationToken ) ;
2016-02-24 23:21:30 +01:00
}
2015-06-18 18:32:52 +02:00
function getOptionsDiagnostics ( ) : Diagnostic [ ] {
2017-06-05 23:11:43 +02:00
return sortAndDeduplicateDiagnostics ( concatenate (
fileProcessingDiagnostics . getGlobalDiagnostics ( ) ,
2017-06-06 02:01:09 +02:00
concatenate (
programDiagnostics . getGlobalDiagnostics ( ) ,
options . configFile ? programDiagnostics . getDiagnostics ( options . configFile . fileName ) : [ ]
)
) ) ;
2015-05-27 05:18:13 +02:00
}
2014-12-16 22:14:14 +01:00
function getGlobalDiagnostics ( ) : Diagnostic [ ] {
2017-06-05 23:11:43 +02:00
return sortAndDeduplicateDiagnostics ( getDiagnosticsProducingTypeChecker ( ) . getGlobalDiagnostics ( ) . slice ( ) ) ;
2014-12-16 22:14:14 +01:00
}
2015-02-04 01:08:46 +01:00
function processRootFile ( fileName : string , isDefaultLib : boolean ) {
2017-09-07 23:31:20 +02:00
processSourceFile ( normalizePath ( fileName ) , isDefaultLib , /*packageId*/ undefined ) ;
2015-10-01 01:10:52 +02:00
}
2015-06-23 02:48:44 +02:00
function fileReferenceIsEqualTo ( a : FileReference , b : FileReference ) : boolean {
return a . fileName === b . fileName ;
}
2015-10-01 01:10:52 +02:00
2017-08-26 00:14:51 +02:00
function module NameIsEqualTo ( a : StringLiteral | Identifier , b : StringLiteral | Identifier ) : boolean {
return a . kind === SyntaxKind . StringLiteral
? b . kind === SyntaxKind . StringLiteral && a . text === b . text
: b . kind === SyntaxKind . Identifier && a . escapedText === b . escapedText ;
2016-01-14 19:56:49 +01:00
}
2015-06-23 02:48:44 +02:00
function collectExternalModuleReferences ( file : SourceFile ) : void {
if ( file . imports ) {
return ;
}
2015-10-01 01:10:52 +02:00
2015-11-09 21:49:36 +01:00
const isJavaScriptFile = isSourceFileJavaScript ( file ) ;
2015-12-22 22:21:51 +01:00
const isExternalModuleFile = isExternalModule ( file ) ;
2015-10-15 02:36:03 +02:00
2017-06-06 02:16:29 +02:00
// file.imports may not be undefined if there exists dynamic import
2017-07-20 15:45:22 +02:00
let imports : StringLiteral [ ] ;
2017-09-23 01:14:41 +02:00
let module Augmentations : ( StringLiteral | Identifier ) [ ] ;
2016-11-02 22:41:26 +01:00
let ambientModules : string [ ] ;
2015-12-22 22:21:51 +01:00
2016-06-15 01:24:01 +02:00
// If we are importing helpers, we need to add a synthetic reference to resolve the
// helpers library.
if ( options . importHelpers
&& ( options . isolatedModules || isExternalModuleFile )
&& ! file . isDeclarationFile ) {
2016-11-10 20:57:37 +01:00
// synthesize 'import "tslib"' declaration
2017-02-02 01:36:10 +01:00
const externalHelpersModuleReference = createLiteral ( externalHelpersModuleNameText ) ;
2017-04-07 22:13:39 +02:00
const importDecl = createImportDeclaration ( /*decorators*/ undefined , /*modifiers*/ undefined , /*importClause*/ undefined ) ;
2018-01-09 01:36:23 +01:00
addEmitFlags ( importDecl , EmitFlags . NeverApplyImportHelper ) ;
2016-11-10 20:57:37 +01:00
externalHelpersModuleReference . parent = importDecl ;
2017-02-02 01:36:10 +01:00
importDecl . parent = file ;
2016-06-15 01:24:01 +02:00
imports = [ externalHelpersModuleReference ] ;
}
2015-11-04 23:02:33 +01:00
for ( const node of file . statements ) {
2015-12-22 22:21:51 +01:00
collectModuleReferences ( node , /*inAmbientModule*/ false ) ;
2017-06-08 23:57:21 +02:00
if ( ( file . flags & NodeFlags . PossiblyContainsDynamicImport ) || isJavaScriptFile ) {
2017-06-06 02:16:29 +02:00
collectDynamicImportOrRequireCalls ( node ) ;
2015-12-22 22:21:51 +01:00
}
2015-09-30 00:06:03 +02:00
}
file . imports = imports || emptyArray ;
2015-12-22 22:21:51 +01:00
file . module Augmentations = module Augmentations || emptyArray ;
2016-11-02 22:41:26 +01:00
file . ambientModuleNames = ambientModules || emptyArray ;
2015-10-01 01:10:52 +02:00
2015-10-22 23:12:57 +02:00
return ;
2017-08-26 00:14:51 +02:00
function collectModuleReferences ( node : Statement , inAmbientModule : boolean ) : void {
2015-12-22 22:21:51 +01:00
switch ( node . kind ) {
case SyntaxKind . ImportDeclaration :
case SyntaxKind . ImportEqualsDeclaration :
case SyntaxKind . ExportDeclaration :
2016-12-18 07:44:54 +01:00
const module NameExpr = getExternalModuleName ( node ) ;
2017-07-20 15:45:22 +02:00
if ( ! module NameExpr || ! isStringLiteral ( module NameExpr ) ) {
2015-12-22 22:21:51 +01:00
break ;
}
2017-07-20 15:45:22 +02:00
if ( ! module NameExpr.text ) {
2015-12-22 22:21:51 +01:00
break ;
}
2015-06-23 02:48:44 +02:00
2015-12-22 22:21:51 +01:00
// TypeScript 1.0 spec (April 2014): 12.1.6
2016-01-30 20:37:02 +01:00
// An ExternalImportDeclaration in an AmbientExternalModuleDeclaration may reference other external modules
2015-12-22 22:21:51 +01:00
// only through top - level external module names. Relative external module names are not permitted.
2017-07-20 15:45:22 +02:00
if ( ! inAmbientModule || ! isExternalModuleNameRelative ( module NameExpr.text ) ) {
( imports || ( imports = [ ] ) ) . push ( module NameExpr ) ;
2015-12-22 22:21:51 +01:00
}
break ;
case SyntaxKind . ModuleDeclaration :
2017-05-17 17:12:23 +02:00
if ( isAmbientModule ( < ModuleDeclaration > node ) && ( inAmbientModule || hasModifier ( node , ModifierFlags . Ambient ) || file . isDeclarationFile ) ) {
2017-08-26 00:14:51 +02:00
const module Name = ( < ModuleDeclaration > node ) . name ;
const nameText = getTextOfIdentifierOrLiteral ( module Name ) ;
2015-12-22 22:21:51 +01:00
// Ambient module declarations can be interpreted as augmentations for some existing external modules.
// This will happen in two cases:
// - if current file is external module then module augmentation is a ambient module declaration defined in the top level scope
// - if current file is not external module then module augmentation is an ambient module declaration with non-relative module name
// immediately nested in top level ambient module declaration .
2017-07-25 22:16:34 +02:00
if ( isExternalModuleFile || ( inAmbientModule && ! isExternalModuleNameRelative ( nameText ) ) ) {
2015-12-22 22:21:51 +01:00
( module Augmentations || ( module Augmentations = [ ] ) ) . push ( module Name ) ;
2015-10-15 02:36:03 +02:00
}
2015-12-22 22:21:51 +01:00
else if ( ! inAmbientModule ) {
2017-05-17 17:12:23 +02:00
if ( file . isDeclarationFile ) {
2016-11-02 22:41:26 +01:00
// for global .d.ts files record name of ambient module
2017-07-25 22:16:34 +02:00
( ambientModules || ( ambientModules = [ ] ) ) . push ( nameText ) ;
2016-11-02 22:41:26 +01:00
}
2016-01-30 20:37:02 +01:00
// An AmbientExternalModuleDeclaration declares an external module.
2015-10-22 23:12:57 +02:00
// This type of declaration is permitted only in the global module.
// The StringLiteral must specify a top - level external module name.
// Relative external module names are not permitted
2015-12-22 22:21:51 +01:00
2016-06-02 18:15:48 +02:00
// NOTE: body of ambient module is always a module block, if it exists
const body = < ModuleBlock > ( < ModuleDeclaration > node ) . body ;
if ( body ) {
for ( const statement of body . statements ) {
collectModuleReferences ( statement , /*inAmbientModule*/ true ) ;
}
2015-12-22 22:21:51 +01:00
}
2015-10-22 23:12:57 +02:00
}
2015-12-22 22:21:51 +01:00
}
2015-06-23 02:48:44 +02:00
}
2015-12-22 22:21:51 +01:00
}
2015-06-23 02:48:44 +02:00
2017-06-06 02:16:29 +02:00
function collectDynamicImportOrRequireCalls ( node : Node ) : void {
2017-04-11 18:37:31 +02:00
if ( isRequireCall ( node , /*checkArgumentIsStringLiteral*/ true ) ) {
2015-12-22 22:21:51 +01:00
( imports || ( imports = [ ] ) ) . push ( < StringLiteral > ( < CallExpression > node ) . arguments [ 0 ] ) ;
}
2017-06-06 02:16:29 +02:00
// we have to check the argument list has length of 1. We will still have to process these even though we have parsing error.
else if ( isImportCall ( node ) && node . arguments . length === 1 && node . arguments [ 0 ] . kind === SyntaxKind . StringLiteral ) {
( imports || ( imports = [ ] ) ) . push ( < StringLiteral > ( < CallExpression > node ) . arguments [ 0 ] ) ;
}
2015-12-22 22:21:51 +01:00
else {
2017-06-06 02:16:29 +02:00
forEachChild ( node , collectDynamicImportOrRequireCalls ) ;
2015-06-23 02:48:44 +02:00
}
}
2014-12-16 22:14:14 +01:00
}
2017-05-09 21:20:52 +02:00
/** This should have similar behavior to 'processSourceFile' without diagnostics or mutation. */
function getSourceFileFromReference ( referencingFile : SourceFile , ref : FileReference ) : SourceFile | undefined {
2017-07-10 20:24:17 +02:00
return getSourceFileFromReferenceWorker ( resolveTripleslashReference ( ref . fileName , referencingFile . fileName ) , fileName = > filesByName . get ( toPath ( fileName ) ) ) ;
2017-05-09 21:20:52 +02:00
}
function getSourceFileFromReferenceWorker (
2017-08-15 03:19:43 +02:00
fileName : string ,
getSourceFile : ( fileName : string ) = > SourceFile | undefined ,
fail ? : ( diagnostic : DiagnosticMessage , . . . argument : string [ ] ) = > void ,
refFile? : SourceFile ) : SourceFile | undefined {
2017-05-09 21:20:52 +02:00
2015-02-04 01:08:46 +01:00
if ( hasExtension ( fileName ) ) {
2015-03-27 00:48:17 +01:00
if ( ! options . allowNonTsExtensions && ! forEach ( supportedExtensions , extension = > fileExtensionIs ( host . getCanonicalFileName ( fileName ) , extension ) ) ) {
2017-05-09 21:20:52 +02:00
if ( fail ) fail ( Diagnostics . File_0_has_unsupported_extension_The_only_supported_extensions_are_1 , fileName , "'" + supportedExtensions . join ( "', '" ) + "'" ) ;
return undefined ;
2014-12-16 22:14:14 +01:00
}
2017-05-09 21:20:52 +02:00
const sourceFile = getSourceFile ( fileName ) ;
if ( fail ) {
if ( ! sourceFile ) {
fail ( Diagnostics . File_0_not_found , fileName ) ;
2015-06-11 23:30:58 +02:00
}
2017-05-09 21:20:52 +02:00
else if ( refFile && host . getCanonicalFileName ( fileName ) === host . getCanonicalFileName ( refFile . fileName ) ) {
2017-09-28 06:25:55 +02:00
fail ( Diagnostics . A_file_cannot_have_a_reference_to_itself ) ;
2015-06-11 23:30:58 +02:00
}
2014-12-16 22:14:14 +01:00
}
2017-05-09 21:20:52 +02:00
return sourceFile ;
2017-06-05 23:23:39 +02:00
}
else {
2017-05-09 21:20:52 +02:00
const sourceFileNoExtension = options . allowNonTsExtensions && getSourceFile ( fileName ) ;
if ( sourceFileNoExtension ) return sourceFileNoExtension ;
2014-12-16 22:14:14 +01:00
2017-05-09 21:20:52 +02:00
if ( fail && options . allowNonTsExtensions ) {
fail ( Diagnostics . File_0_not_found , fileName ) ;
return undefined ;
2014-12-16 22:14:14 +01:00
}
2017-05-09 21:20:52 +02:00
const sourceFileWithAddedExtension = forEach ( supportedExtensions , extension = > getSourceFile ( fileName + extension ) ) ;
2017-06-10 04:32:44 +02:00
if ( fail && ! sourceFileWithAddedExtension ) fail ( Diagnostics . File_0_not_found , fileName + Extension . Ts ) ;
2017-05-09 21:20:52 +02:00
return sourceFileWithAddedExtension ;
2014-12-16 22:14:14 +01:00
}
}
2017-05-09 21:20:52 +02:00
/** This has side effects through `findSourceFile`. */
2017-09-07 23:31:20 +02:00
function processSourceFile ( fileName : string , isDefaultLib : boolean , packageId : PackageId | undefined , refFile? : SourceFile , refPos? : number , refEnd? : number ) : void {
2017-05-09 21:20:52 +02:00
getSourceFileFromReferenceWorker ( fileName ,
2017-09-07 23:31:20 +02:00
fileName = > findSourceFile ( fileName , toPath ( fileName ) , isDefaultLib , refFile , refPos , refEnd , packageId ) ,
2017-05-09 21:20:52 +02:00
( diagnostic , . . . args ) = > {
fileProcessingDiagnostics . add ( refFile !== undefined && refEnd !== undefined && refPos !== undefined
? createFileDiagnostic ( refFile , refPos , refEnd - refPos , diagnostic , . . . args )
: createCompilerDiagnostic ( diagnostic , . . . args ) ) ;
} ,
refFile ) ;
}
2015-10-15 23:43:51 +02:00
function reportFileNamesDifferOnlyInCasingError ( fileName : string , existingFileName : string , refFile : SourceFile , refPos : number , refEnd : number ) : void {
if ( refFile !== undefined && refPos !== undefined && refEnd !== undefined ) {
fileProcessingDiagnostics . add ( createFileDiagnostic ( refFile , refPos , refEnd - refPos ,
Diagnostics . File_name_0_differs_from_already_included_file_name_1_only_in_casing , fileName , existingFileName ) ) ;
2014-12-16 22:14:14 +01:00
}
2015-10-15 23:43:51 +02:00
else {
fileProcessingDiagnostics . add ( createCompilerDiagnostic ( Diagnostics . File_name_0_differs_from_already_included_file_name_1_only_in_casing , fileName , existingFileName ) ) ;
2015-09-27 06:29:07 +02:00
}
2015-10-15 23:43:51 +02:00
}
2015-10-01 01:10:52 +02:00
2017-08-09 23:39:06 +02:00
function createRedirectSourceFile ( redirectTarget : SourceFile , unredirected : SourceFile , fileName : string , path : Path ) : SourceFile {
const redirect : SourceFile = Object . create ( redirectTarget ) ;
redirect . fileName = fileName ;
redirect . path = path ;
redirect . redirectInfo = { redirectTarget , unredirected } ;
Object . defineProperties ( redirect , {
id : {
get ( this : SourceFile ) { return this . redirectInfo . redirectTarget . id ; } ,
set ( this : SourceFile , value : SourceFile [ "id" ] ) { this . redirectInfo . redirectTarget . id = value ; } ,
} ,
symbol : {
get ( this : SourceFile ) { return this . redirectInfo . redirectTarget . symbol ; } ,
set ( this : SourceFile , value : SourceFile [ "symbol" ] ) { this . redirectInfo . redirectTarget . symbol = value ; } ,
} ,
} ) ;
return redirect ;
}
2015-10-15 23:43:51 +02:00
// Get source file from normalized fileName
2017-08-09 23:39:06 +02:00
function findSourceFile ( fileName : string , path : Path , isDefaultLib : boolean , refFile : SourceFile , refPos : number , refEnd : number , packageId : PackageId | undefined ) : SourceFile | undefined {
2017-06-28 22:15:34 +02:00
if ( filesByName . has ( path ) ) {
2016-10-28 00:50:21 +02:00
const file = filesByName . get ( path ) ;
2015-10-15 23:43:51 +02:00
// try to check if we've already seen this file but with a different casing in path
// NOTE: this only makes sense for case-insensitive file systems
2015-11-16 18:49:58 +01:00
if ( file && options . forceConsistentCasingInFileNames && getNormalizedAbsolutePath ( file . fileName , currentDirectory ) !== getNormalizedAbsolutePath ( fileName , currentDirectory ) ) {
2015-10-15 23:43:51 +02:00
reportFileNamesDifferOnlyInCasingError ( fileName , file . fileName , refFile , refPos , refEnd ) ;
}
2016-07-11 01:11:42 +02:00
// If the file was previously found via a node_modules search, but is now being processed as a root file,
// then everything it sucks in may also be marked incorrectly, and needs to be checked again.
2017-04-05 00:51:13 +02:00
if ( file && sourceFilesFoundSearchingNodeModules . get ( file . path ) && currentNodeModulesDepth === 0 ) {
2016-12-05 23:13:32 +01:00
sourceFilesFoundSearchingNodeModules . set ( file . path , false ) ;
2016-07-11 01:11:42 +02:00
if ( ! options . noResolve ) {
2016-10-10 22:06:58 +02:00
processReferencedFiles ( file , isDefaultLib ) ;
2016-07-11 01:11:42 +02:00
processTypeReferenceDirectives ( file ) ;
}
2016-12-05 23:13:32 +01:00
module sWithElidedImports.set ( file . path , false ) ;
2016-10-10 22:06:58 +02:00
processImportedModules ( file ) ;
2016-07-11 01:11:42 +02:00
}
2016-06-27 05:48:22 +02:00
// See if we need to reprocess the imports due to prior skipped imports
2016-12-05 23:13:32 +01:00
else if ( file && module sWithElidedImports.get ( file . path ) ) {
2016-10-13 22:02:22 +02:00
if ( currentNodeModulesDepth < maxNodeModuleJsDepth ) {
2016-12-05 23:13:32 +01:00
module sWithElidedImports.set ( file . path , false ) ;
2016-10-10 22:06:58 +02:00
processImportedModules ( file ) ;
2016-06-27 05:48:22 +02:00
}
}
2015-09-27 06:29:07 +02:00
return file ;
2014-12-16 22:14:14 +01:00
}
2015-09-27 06:29:07 +02:00
// We haven't looked for this file, do so now and cache result
2016-10-28 00:50:21 +02:00
const file = host . getSourceFile ( fileName , options . target , hostErrorMessage = > {
2015-09-27 06:29:07 +02:00
if ( refFile !== undefined && refPos !== undefined && refEnd !== undefined ) {
fileProcessingDiagnostics . add ( createFileDiagnostic ( refFile , refPos , refEnd - refPos ,
Diagnostics . Cannot_read_file_0_Colon_1 , fileName , hostErrorMessage ) ) ;
}
else {
fileProcessingDiagnostics . add ( createCompilerDiagnostic ( Diagnostics . Cannot_read_file_0_Colon_1 , fileName , hostErrorMessage ) ) ;
}
2017-07-25 01:57:49 +02:00
} , shouldCreateNewSourceFile ) ;
2015-10-01 01:10:52 +02:00
2017-08-09 23:39:06 +02:00
if ( packageId ) {
2018-01-17 20:41:23 +01:00
const packageIdKey = packageIdToString ( packageId ) ;
2017-08-09 23:39:06 +02:00
const fileFromPackageId = packageIdToSourceFile . get ( packageIdKey ) ;
if ( fileFromPackageId ) {
// Some other SourceFile already exists with this package name and version.
// Instead of creating a duplicate, just redirect to the existing one.
const dupFile = createRedirectSourceFile ( fileFromPackageId , file , fileName , path ) ;
redirectTargetsSet . set ( fileFromPackageId . path , true ) ;
filesByName . set ( path , dupFile ) ;
sourceFileToPackageName . set ( path , packageId . name ) ;
files . push ( dupFile ) ;
return dupFile ;
}
else if ( file ) {
// This is the first source file to have this packageId.
packageIdToSourceFile . set ( packageIdKey , file ) ;
sourceFileToPackageName . set ( path , packageId . name ) ;
}
}
2015-11-16 18:49:58 +01:00
filesByName . set ( path , file ) ;
2015-09-27 06:29:07 +02:00
if ( file ) {
2016-12-05 23:13:32 +01:00
sourceFilesFoundSearchingNodeModules . set ( path , currentNodeModulesDepth > 0 ) ;
2015-11-16 18:49:58 +01:00
file . path = path ;
2015-10-29 22:54:56 +01:00
2015-10-15 23:43:51 +02:00
if ( host . useCaseSensitiveFileNames ( ) ) {
2017-07-10 20:24:17 +02:00
const pathLowerCase = path . toLowerCase ( ) ;
2015-10-15 23:43:51 +02:00
// for case-sensitive file systems check if we've already seen some file with similar filename ignoring case
2017-07-10 20:24:17 +02:00
const existingFile = filesByNameIgnoreCase . get ( pathLowerCase ) ;
2015-10-15 23:43:51 +02:00
if ( existingFile ) {
reportFileNamesDifferOnlyInCasingError ( fileName , existingFile . fileName , refFile , refPos , refEnd ) ;
}
else {
2017-07-10 20:24:17 +02:00
filesByNameIgnoreCase . set ( pathLowerCase , file ) ;
2015-10-15 23:43:51 +02:00
}
}
2014-12-16 22:14:14 +01:00
2015-10-15 23:43:51 +02:00
skipDefaultLib = skipDefaultLib || file . hasNoDefaultLib ;
2015-10-01 01:10:52 +02:00
2015-09-27 06:29:07 +02:00
if ( ! options . noResolve ) {
2016-10-10 22:06:58 +02:00
processReferencedFiles ( file , isDefaultLib ) ;
2016-04-06 01:33:11 +02:00
processTypeReferenceDirectives ( file ) ;
2015-09-27 06:29:07 +02:00
}
2015-08-19 20:58:02 +02:00
2015-09-27 06:29:07 +02:00
// always process imported modules to record module name resolutions
2016-10-10 22:06:58 +02:00
processImportedModules ( file ) ;
2015-08-19 20:58:02 +02:00
2015-09-27 06:29:07 +02:00
if ( isDefaultLib ) {
files . unshift ( file ) ;
}
else {
files . push ( file ) ;
2014-12-16 22:14:14 +01:00
}
}
2015-09-27 06:29:07 +02:00
return file ;
2014-12-16 22:14:14 +01:00
}
2016-10-10 22:06:58 +02:00
function processReferencedFiles ( file : SourceFile , isDefaultLib : boolean ) {
2014-12-16 22:14:14 +01:00
forEach ( file . referencedFiles , ref = > {
2015-11-04 23:02:33 +01:00
const referencedFileName = resolveTripleslashReference ( ref . fileName , file . fileName ) ;
2017-09-07 23:31:20 +02:00
processSourceFile ( referencedFileName , isDefaultLib , /*packageId*/ undefined , file , ref . pos , ref . end ) ;
2014-12-16 22:14:14 +01:00
} ) ;
}
2015-10-01 01:10:52 +02:00
2016-04-06 01:33:11 +02:00
function processTypeReferenceDirectives ( file : SourceFile ) {
2016-08-17 15:21:49 +02:00
// We lower-case all type references because npm automatically lowercases all packages. See GH#9824.
2016-08-15 16:40:25 +02:00
const typeDirectives = map ( file . typeReferenceDirectives , ref = > ref . fileName . toLocaleLowerCase ( ) ) ;
2016-04-01 21:41:01 +02:00
const resolutions = resolveTypeReferenceDirectiveNamesWorker ( typeDirectives , file . fileName ) ;
2016-02-23 21:48:31 +01:00
2016-04-01 21:41:01 +02:00
for ( let i = 0 ; i < typeDirectives . length ; i ++ ) {
const ref = file . typeReferenceDirectives [ i ] ;
const resolvedTypeReferenceDirective = resolutions [ i ] ;
// store resolved type directive on the file
2016-08-15 16:40:25 +02:00
const fileName = ref . fileName . toLocaleLowerCase ( ) ;
setResolvedTypeReferenceDirective ( file , fileName , resolvedTypeReferenceDirective ) ;
processTypeReferenceDirective ( fileName , resolvedTypeReferenceDirective , file , ref . pos , ref . end ) ;
2016-04-06 22:49:25 +02:00
}
}
function processTypeReferenceDirective ( typeReferenceDirective : string , resolvedTypeReferenceDirective : ResolvedTypeReferenceDirective ,
refFile? : SourceFile , refPos? : number , refEnd? : number ) : void {
// If we already found this library as a primary reference - nothing to do
2016-12-05 23:13:32 +01:00
const previousResolution = resolvedTypeReferenceDirectives . get ( typeReferenceDirective ) ;
2016-04-06 22:49:25 +02:00
if ( previousResolution && previousResolution . primary ) {
return ;
}
let saveResolution = true ;
if ( resolvedTypeReferenceDirective ) {
if ( resolvedTypeReferenceDirective . primary ) {
// resolved from the primary path
2017-09-07 23:31:20 +02:00
processSourceFile ( resolvedTypeReferenceDirective . resolvedFileName , /*isDefaultLib*/ false , resolvedTypeReferenceDirective . packageId , refFile , refPos , refEnd ) ;
2016-02-23 21:48:31 +01:00
}
2016-04-06 22:49:25 +02:00
else {
// If we already resolved to this file, it must have been a secondary reference. Check file contents
// for sameness and possibly issue an error
if ( previousResolution ) {
2016-11-09 16:41:25 +01:00
// Don't bother reading the file again if it's the same file.
if ( resolvedTypeReferenceDirective . resolvedFileName !== previousResolution . resolvedFileName ) {
const otherFileText = host . readFile ( resolvedTypeReferenceDirective . resolvedFileName ) ;
if ( otherFileText !== getSourceFile ( previousResolution . resolvedFileName ) . text ) {
fileProcessingDiagnostics . add ( createDiagnostic ( refFile , refPos , refEnd ,
Diagnostics . Conflicting_definitions_for_0_found_at_1_and_2_Consider_installing_a_specific_version_of_this_library_to_resolve_the_conflict ,
typeReferenceDirective ,
resolvedTypeReferenceDirective . resolvedFileName ,
previousResolution . resolvedFileName
) ) ;
}
2016-04-06 22:49:25 +02:00
}
// don't overwrite previous resolution result
saveResolution = false ;
2016-02-23 21:48:31 +01:00
}
2016-04-01 21:41:01 +02:00
else {
2016-04-06 22:49:25 +02:00
// First resolution of this library
2017-09-07 23:31:20 +02:00
processSourceFile ( resolvedTypeReferenceDirective . resolvedFileName , /*isDefaultLib*/ false , resolvedTypeReferenceDirective . packageId , refFile , refPos , refEnd ) ;
2016-02-23 21:48:31 +01:00
}
}
2016-04-06 22:49:25 +02:00
}
else {
2016-06-11 00:44:11 +02:00
fileProcessingDiagnostics . add ( createDiagnostic ( refFile , refPos , refEnd , Diagnostics . Cannot_find_type_definition_file_for_0 , typeReferenceDirective ) ) ;
2016-04-06 22:49:25 +02:00
}
2016-02-23 21:48:31 +01:00
2016-04-06 22:49:25 +02:00
if ( saveResolution ) {
2016-12-05 23:13:32 +01:00
resolvedTypeReferenceDirectives . set ( typeReferenceDirective , resolvedTypeReferenceDirective ) ;
2016-04-06 22:49:25 +02:00
}
}
function createDiagnostic ( refFile : SourceFile , refPos : number , refEnd : number , message : DiagnosticMessage , . . . args : any [ ] ) : Diagnostic {
if ( refFile === undefined || refPos === undefined || refEnd === undefined ) {
return createCompilerDiagnostic ( message , . . . args ) ;
}
else {
return createFileDiagnostic ( refFile , refPos , refEnd - refPos , message , . . . args ) ;
2016-04-01 21:41:01 +02:00
}
2016-02-23 21:48:31 +01:00
}
2015-10-15 23:43:51 +02:00
function getCanonicalFileName ( fileName : string ) : string {
return host . getCanonicalFileName ( fileName ) ;
}
2016-10-10 22:06:58 +02:00
function processImportedModules ( file : SourceFile ) {
2015-08-20 00:37:37 +02:00
collectExternalModuleReferences ( file ) ;
2015-12-22 22:21:51 +01:00
if ( file . imports . length || file . module Augmentations.length ) {
2016-11-10 23:28:34 +01:00
// Because global augmentation doesn't have string literal name, we can check for global augmentation as such.
2017-08-26 00:14:51 +02:00
const module Names = getModuleNames ( file ) ;
2017-12-06 22:17:21 +01:00
const oldProgramState : OldProgramState = { program : oldProgram , oldSourceFile : oldProgram && oldProgram . getSourceFile ( file . fileName ) , modifiedFilePaths } ;
2017-04-19 00:48:01 +02:00
const resolutions = resolveModuleNamesReusingOldState ( module Names , getNormalizedAbsolutePath ( file . fileName , currentDirectory ) , file , oldProgramState ) ;
2016-10-13 22:02:22 +02:00
Debug . assert ( resolutions . length === module Names.length ) ;
2016-01-12 07:34:38 +01:00
for ( let i = 0 ; i < module Names.length ; i ++ ) {
2015-11-04 23:02:33 +01:00
const resolution = resolutions [ i ] ;
2015-09-10 20:36:31 +02:00
setResolvedModule ( file , module Names [ i ] , resolution ) ;
2016-06-27 05:48:22 +02:00
2016-10-13 22:02:22 +02:00
if ( ! resolution ) {
continue ;
}
const isFromNodeModulesSearch = resolution . isExternalLibraryImport ;
2017-09-19 21:42:29 +02:00
const isJsFile = ! extensionIsTypeScript ( resolution . extension ) ;
const isJsFileFromNodeModules = isFromNodeModulesSearch && isJsFile ;
2016-10-18 23:22:43 +02:00
const resolvedFileName = resolution . resolvedFileName ;
2016-06-27 05:48:22 +02:00
2016-06-29 17:44:06 +02:00
if ( isFromNodeModulesSearch ) {
2016-07-11 01:11:42 +02:00
currentNodeModulesDepth ++ ;
2016-06-21 22:42:02 +02:00
}
2015-12-22 22:21:51 +01:00
2016-10-13 22:02:22 +02:00
// add file to program only if:
// - resolution was successful
// - noResolve is falsy
// - module name comes from the list of imports
// - it's not a top level JavaScript module that exceeded the search max
const elideImport = isJsFileFromNodeModules && currentNodeModulesDepth > maxNodeModuleJsDepth ;
2016-10-18 23:22:43 +02:00
// Don't add the file if it has a bad extension (e.g. 'tsx' if we don't have '--allowJs')
2016-10-27 16:19:37 +02:00
// This may still end up being an untyped module -- the file won't be included but imports will be allowed.
2017-09-19 21:42:29 +02:00
const shouldAddFile = resolvedFileName
&& ! getResolutionDiagnostic ( options , resolution )
&& ! options . noResolve
&& i < file . imports . length
&& ! elideImport
&& ! ( isJsFile && ! options . allowJs ) ;
2016-06-27 05:48:22 +02:00
if ( elideImport ) {
2016-12-05 23:13:32 +01:00
module sWithElidedImports.set ( file . path , true ) ;
2016-06-27 05:48:22 +02:00
}
else if ( shouldAddFile ) {
2017-07-10 20:24:17 +02:00
const path = toPath ( resolvedFileName ) ;
2016-10-13 22:02:22 +02:00
const pos = skipTrivia ( file . text , file . imports [ i ] . pos ) ;
2017-08-09 23:39:06 +02:00
findSourceFile ( resolvedFileName , path , /*isDefaultLib*/ false , file , pos , file . imports [ i ] . end , resolution . packageId ) ;
2016-06-21 22:42:02 +02:00
}
2015-12-22 22:21:51 +01:00
2016-07-11 01:11:42 +02:00
if ( isFromNodeModulesSearch ) {
currentNodeModulesDepth -- ;
2015-08-04 02:42:29 +02:00
}
2015-08-19 20:58:02 +02:00
}
2015-06-23 02:48:44 +02:00
}
else {
// no imports - drop cached module resolutions
file . resolvedModules = undefined ;
}
2014-12-16 22:14:14 +01:00
}
2016-02-23 21:48:31 +01:00
function computeCommonSourceDirectory ( sourceFiles : SourceFile [ ] ) : string {
const fileNames : string [ ] = [ ] ;
for ( const file of sourceFiles ) {
if ( ! file . isDeclarationFile ) {
fileNames . push ( file . fileName ) ;
}
}
2016-04-01 21:41:01 +02:00
return computeCommonSourceDirectoryOfFilenames ( fileNames , currentDirectory , getCanonicalFileName ) ;
2016-02-23 21:48:31 +01:00
}
2015-04-19 22:24:39 +02:00
function checkSourceFilesBelongToPath ( sourceFiles : SourceFile [ ] , rootDirectory : string ) : boolean {
2015-04-15 07:11:25 +02:00
let allFilesBelongToPath = true ;
2015-04-19 22:24:39 +02:00
if ( sourceFiles ) {
2015-11-04 23:02:33 +01:00
const absoluteRootDirectoryPath = host . getCanonicalFileName ( getNormalizedAbsolutePath ( rootDirectory , currentDirectory ) ) ;
2015-04-15 07:11:25 +02:00
2016-01-16 23:05:46 +01:00
for ( const sourceFile of sourceFiles ) {
2017-05-17 17:12:23 +02:00
if ( ! sourceFile . isDeclarationFile ) {
2015-11-04 23:02:33 +01:00
const absoluteSourceFilePath = host . getCanonicalFileName ( getNormalizedAbsolutePath ( sourceFile . fileName , currentDirectory ) ) ;
2015-04-15 07:11:25 +02:00
if ( absoluteSourceFilePath . indexOf ( absoluteRootDirectoryPath ) !== 0 ) {
2015-09-10 19:46:39 +02:00
programDiagnostics . add ( createCompilerDiagnostic ( Diagnostics . File_0_is_not_under_rootDir_1_rootDir_is_expected_to_contain_all_source_files , sourceFile . fileName , options . rootDir ) ) ;
2015-04-15 07:11:25 +02:00
allFilesBelongToPath = false ;
}
}
}
2015-04-15 00:05:08 +02:00
}
2015-04-15 07:11:25 +02:00
return allFilesBelongToPath ;
2015-04-15 00:05:08 +02:00
}
2014-12-16 22:14:14 +01:00
function verifyCompilerOptions() {
2015-05-19 06:49:41 +02:00
if ( options . isolatedModules ) {
2015-03-31 04:33:15 +02:00
if ( options . declaration ) {
2016-11-29 21:42:17 +01:00
createDiagnosticForOptionName ( Diagnostics . Option_0_cannot_be_specified_with_option_1 , "declaration" , "isolatedModules" ) ;
2015-03-31 04:33:15 +02:00
}
if ( options . noEmitOnError ) {
2016-11-29 21:42:17 +01:00
createDiagnosticForOptionName ( Diagnostics . Option_0_cannot_be_specified_with_option_1 , "noEmitOnError" , "isolatedModules" ) ;
2015-03-31 04:33:15 +02:00
}
if ( options . out ) {
2016-11-29 21:42:17 +01:00
createDiagnosticForOptionName ( Diagnostics . Option_0_cannot_be_specified_with_option_1 , "out" , "isolatedModules" ) ;
2015-03-31 04:33:15 +02:00
}
2015-08-21 02:37:56 +02:00
if ( options . outFile ) {
2016-11-29 21:42:17 +01:00
createDiagnosticForOptionName ( Diagnostics . Option_0_cannot_be_specified_with_option_1 , "outFile" , "isolatedModules" ) ;
2015-03-31 04:33:15 +02:00
}
}
2015-04-08 09:14:23 +02:00
if ( options . inlineSourceMap ) {
if ( options . sourceMap ) {
2016-11-29 21:42:17 +01:00
createDiagnosticForOptionName ( Diagnostics . Option_0_cannot_be_specified_with_option_1 , "sourceMap" , "inlineSourceMap" ) ;
2015-04-08 09:14:23 +02:00
}
if ( options . mapRoot ) {
2016-11-29 21:42:17 +01:00
createDiagnosticForOptionName ( Diagnostics . Option_0_cannot_be_specified_with_option_1 , "mapRoot" , "inlineSourceMap" ) ;
2015-04-08 09:14:23 +02:00
}
2015-04-21 05:33:31 +02:00
}
2016-01-25 20:49:26 +01:00
if ( options . paths && options . baseUrl === undefined ) {
2016-11-29 21:42:17 +01:00
createDiagnosticForOptionName ( Diagnostics . Option_paths_cannot_be_used_without_specifying_baseUrl_option , "paths" ) ;
2015-11-19 06:46:45 +01:00
}
if ( options . paths ) {
for ( const key in options . paths ) {
if ( ! hasProperty ( options . paths , key ) ) {
continue ;
}
if ( ! hasZeroOrOneAsteriskCharacter ( key ) ) {
2016-11-29 21:42:17 +01:00
createDiagnosticForOptionPaths ( /*onKey*/ true , key , Diagnostics . Pattern_0_can_have_at_most_one_Asterisk_character , key ) ;
2015-11-19 06:46:45 +01:00
}
2016-04-14 06:16:39 +02:00
if ( isArray ( options . paths [ key ] ) ) {
2016-11-29 21:42:17 +01:00
const len = options . paths [ key ] . length ;
if ( len === 0 ) {
createDiagnosticForOptionPaths ( /*onKey*/ false , key , Diagnostics . Substitutions_for_pattern_0_shouldn_t_be_an_empty_array , key ) ;
2016-07-23 08:05:36 +02:00
}
2016-11-29 21:42:17 +01:00
for ( let i = 0 ; i < len ; i ++ ) {
const subst = options . paths [ key ] [ i ] ;
2016-04-14 06:16:39 +02:00
const typeOfSubst = typeof subst ;
if ( typeOfSubst === "string" ) {
if ( ! hasZeroOrOneAsteriskCharacter ( subst ) ) {
2016-11-29 21:42:17 +01:00
createDiagnosticForOptionPathKeyValue ( key , i , Diagnostics . Substitution_0_in_pattern_1_in_can_have_at_most_one_Asterisk_character , subst , key ) ;
2016-04-14 06:16:39 +02:00
}
}
else {
2016-11-29 21:42:17 +01:00
createDiagnosticForOptionPathKeyValue ( key , i , Diagnostics . Substitution_0_for_pattern_1_has_incorrect_type_expected_string_got_2 , subst , key , typeOfSubst ) ;
2016-04-14 06:16:39 +02:00
}
2015-11-19 06:46:45 +01:00
}
}
2016-04-14 06:16:39 +02:00
else {
2016-11-29 21:42:17 +01:00
createDiagnosticForOptionPaths ( /*onKey*/ false , key , Diagnostics . Substitutions_for_pattern_0_should_be_an_array , key ) ;
2016-04-14 06:16:39 +02:00
}
2015-11-19 06:46:45 +01:00
}
}
2016-06-09 20:51:53 +02:00
if ( ! options . sourceMap && ! options . inlineSourceMap ) {
if ( options . inlineSources ) {
2016-11-29 21:42:17 +01:00
createDiagnosticForOptionName ( Diagnostics . Option_0_can_only_be_used_when_either_option_inlineSourceMap_or_option_sourceMap_is_provided , "inlineSources" ) ;
2015-04-08 09:14:23 +02:00
}
2015-11-11 21:58:31 +01:00
if ( options . sourceRoot ) {
2016-11-29 21:42:17 +01:00
createDiagnosticForOptionName ( Diagnostics . Option_0_can_only_be_used_when_either_option_inlineSourceMap_or_option_sourceMap_is_provided , "sourceRoot" ) ;
2015-11-11 21:58:31 +01:00
}
2015-04-08 09:14:23 +02:00
}
2015-08-21 02:37:56 +02:00
if ( options . out && options . outFile ) {
2016-11-29 21:42:17 +01:00
createDiagnosticForOptionName ( Diagnostics . Option_0_cannot_be_specified_with_option_1 , "out" , "outFile" ) ;
2015-08-21 02:37:56 +02:00
}
2016-06-09 20:51:53 +02:00
if ( options . mapRoot && ! options . sourceMap ) {
// Error to specify --mapRoot without --sourcemap
2016-11-29 21:42:17 +01:00
createDiagnosticForOptionName ( Diagnostics . Option_0_cannot_be_specified_without_specifying_option_1 , "mapRoot" , "sourceMap" ) ;
2014-12-16 22:14:14 +01:00
}
2016-02-23 21:12:52 +01:00
if ( options . declarationDir ) {
if ( ! options . declaration ) {
2016-11-29 21:42:17 +01:00
createDiagnosticForOptionName ( Diagnostics . Option_0_cannot_be_specified_without_specifying_option_1 , "declarationDir" , "declaration" ) ;
2016-02-23 21:12:52 +01:00
}
if ( options . out || options . outFile ) {
2016-11-29 21:42:17 +01:00
createDiagnosticForOptionName ( Diagnostics . Option_0_cannot_be_specified_with_option_1 , "declarationDir" , options . out ? "out" : "outFile" ) ;
2016-02-23 21:12:52 +01:00
}
2016-02-20 23:40:39 +01:00
}
2016-03-28 23:20:29 +02:00
if ( options . lib && options . noLib ) {
2016-11-29 21:42:17 +01:00
createDiagnosticForOptionName ( Diagnostics . Option_0_cannot_be_specified_with_option_1 , "lib" , "noLib" ) ;
2016-03-28 23:20:29 +02:00
}
2017-10-30 21:05:54 +01:00
if ( options . noImplicitUseStrict && getStrictOptionValue ( options , "alwaysStrict" ) ) {
2016-11-29 21:42:17 +01:00
createDiagnosticForOptionName ( Diagnostics . Option_0_cannot_be_specified_with_option_1 , "noImplicitUseStrict" , "alwaysStrict" ) ;
2016-10-14 01:54:09 +02:00
}
2015-11-04 23:02:33 +01:00
const languageVersion = options . target || ScriptTarget . ES3 ;
const outFile = options . outFile || options . out ;
2015-02-13 02:54:30 +01:00
2017-05-17 17:12:23 +02:00
const firstNonAmbientExternalModuleSourceFile = forEach ( files , f = > isExternalModule ( f ) && ! f . isDeclarationFile ? f : undefined ) ;
2015-05-19 06:49:41 +02:00
if ( options . isolatedModules ) {
2016-10-13 13:32:00 +02:00
if ( options . module === ModuleKind . None && languageVersion < ScriptTarget . ES2015 ) {
2016-11-29 21:42:17 +01:00
createDiagnosticForOptionName ( Diagnostics . Option_isolatedModules_can_only_be_used_when_either_option_module_is_provided_or_option_target_is_ES2015_or_higher , "isolatedModules" , "target" ) ;
2016-02-11 20:01:10 +01:00
}
2017-05-17 17:12:23 +02:00
const firstNonExternalModuleSourceFile = forEach ( files , f = > ! isExternalModule ( f ) && ! f . isDeclarationFile ? f : undefined ) ;
2015-03-31 04:33:15 +02:00
if ( firstNonExternalModuleSourceFile ) {
2015-11-04 23:02:33 +01:00
const span = getErrorSpanForNode ( firstNonExternalModuleSourceFile , firstNonExternalModuleSourceFile ) ;
2015-09-10 19:46:39 +02:00
programDiagnostics . add ( createFileDiagnostic ( firstNonExternalModuleSourceFile , span . start , span . length , Diagnostics . Cannot_compile_namespaces_when_the_isolatedModules_flag_is_provided ) ) ;
2015-03-31 04:33:15 +02:00
}
}
2016-10-13 13:32:00 +02:00
else if ( firstNonAmbientExternalModuleSourceFile && languageVersion < ScriptTarget . ES2015 && options . module === ModuleKind . None ) {
2016-02-11 20:01:10 +01:00
// We cannot use createDiagnosticFromNode because nodes do not have parents yet
2016-07-23 00:41:52 +02:00
const span = getErrorSpanForNode ( firstNonAmbientExternalModuleSourceFile , firstNonAmbientExternalModuleSourceFile . externalModuleIndicator ) ;
programDiagnostics . add ( createFileDiagnostic ( firstNonAmbientExternalModuleSourceFile , span . start , span . length , Diagnostics . Cannot_use_imports_exports_or_module_augmentations_when_module_is_none ) ) ;
2016-02-11 20:01:10 +01:00
}
2015-03-12 06:53:36 +01:00
2015-10-05 23:25:48 +02:00
// Cannot specify module gen that isn't amd or system with --out
2016-05-18 02:14:51 +02:00
if ( outFile ) {
if ( options . module && ! ( options . module === ModuleKind . AMD || options . module === ModuleKind . System ) ) {
2016-11-29 21:42:17 +01:00
createDiagnosticForOptionName ( Diagnostics . Only_amd_and_system_modules_are_supported_alongside_0 , options . out ? "out" : "outFile" , "module" ) ;
2016-05-18 02:14:51 +02:00
}
2016-07-23 00:41:52 +02:00
else if ( options . module === undefined && firstNonAmbientExternalModuleSourceFile ) {
const span = getErrorSpanForNode ( firstNonAmbientExternalModuleSourceFile , firstNonAmbientExternalModuleSourceFile . externalModuleIndicator ) ;
programDiagnostics . add ( createFileDiagnostic ( firstNonAmbientExternalModuleSourceFile , span . start , span . length , Diagnostics . Cannot_compile_modules_using_option_0_unless_the_module_flag_is_amd_or_system , options . out ? "out" : "outFile" ) ) ;
2016-05-18 02:14:51 +02:00
}
2015-10-03 02:03:29 +02:00
}
2015-10-22 00:27:33 +02:00
// there has to be common source directory if user specified --outdir || --sourceRoot
// if user specified --mapRoot, there needs to be common source directory if there would be multiple files being emitted
if ( options . outDir || // there is --outDir specified
options . sourceRoot || // there is --sourceRoot specified
options . mapRoot ) { // there is --mapRoot specified
2015-11-19 02:10:22 +01:00
// Precalculate and cache the common source directory
const dir = getCommonSourceDirectory ( ) ;
2015-10-22 00:27:33 +02:00
2015-11-19 02:10:22 +01:00
// If we failed to find a good common directory, but outDir is specified and at least one of our files is on a windows drive/URL/other resource, add a failure
if ( options . outDir && dir === "" && forEach ( files , file = > getRootLength ( file . fileName ) > 1 ) ) {
2016-11-29 21:42:17 +01:00
createDiagnosticForOptionName ( Diagnostics . Cannot_find_the_common_subdirectory_path_for_the_input_files , "outDir" ) ;
2015-11-18 22:19:56 +01:00
}
2015-10-22 00:27:33 +02:00
}
2016-03-12 21:14:00 +01:00
if ( ! options . noEmit && options . allowJs && options . declaration ) {
2016-11-29 21:42:17 +01:00
createDiagnosticForOptionName ( Diagnostics . Option_0_cannot_be_specified_with_option_1 , "allowJs" , "declaration" ) ;
2015-10-30 19:50:07 +01:00
}
2015-06-26 01:24:41 +02:00
2017-03-07 22:49:52 +01:00
if ( options . checkJs && ! options . allowJs ) {
programDiagnostics . add ( createCompilerDiagnostic ( Diagnostics . Option_0_cannot_be_specified_without_specifying_option_1 , "checkJs" , "allowJs" ) ) ;
}
2018-02-05 23:48:50 +01:00
if ( options . emitDeclarationOnly ) {
2018-01-26 00:35:18 +01:00
if ( ! options . declaration ) {
2018-03-09 20:37:10 +01:00
createDiagnosticForOptionName ( Diagnostics . Option_0_cannot_be_specified_without_specifying_option_1 , "emitDeclarationOnly" , "declaration" ) ;
2018-01-26 00:35:18 +01:00
}
if ( options . noEmit ) {
2018-02-05 23:48:50 +01:00
createDiagnosticForOptionName ( Diagnostics . Option_0_cannot_be_specified_with_option_1 , "emitDeclarationOnly" , "noEmit" ) ;
2018-01-26 00:35:18 +01:00
}
}
2015-06-02 00:01:24 +02:00
if ( options . emitDecoratorMetadata &&
! options . experimentalDecorators ) {
2016-11-29 21:42:17 +01:00
createDiagnosticForOptionName ( Diagnostics . Option_0_cannot_be_specified_without_specifying_option_1 , "emitDecoratorMetadata" , "experimentalDecorators" ) ;
2015-06-02 00:01:24 +02:00
}
2015-07-09 00:35:49 +02:00
2016-11-09 21:12:48 +01:00
if ( options . jsxFactory ) {
if ( options . reactNamespace ) {
2016-11-29 21:42:17 +01:00
createDiagnosticForOptionName ( Diagnostics . Option_0_cannot_be_specified_with_option_1 , "reactNamespace" , "jsxFactory" ) ;
2016-11-09 21:12:48 +01:00
}
2016-11-10 17:53:32 +01:00
if ( ! parseIsolatedEntityName ( options . jsxFactory , languageVersion ) ) {
2016-11-29 21:42:17 +01:00
createOptionValueDiagnostic ( "jsxFactory" , Diagnostics . Invalid_value_for_jsxFactory_0_is_not_a_valid_identifier_or_qualified_name , options . jsxFactory ) ;
2016-11-09 22:15:13 +01:00
}
2016-11-09 21:12:48 +01:00
}
else if ( options . reactNamespace && ! isIdentifierText ( options . reactNamespace , languageVersion ) ) {
2016-11-29 21:42:17 +01:00
createOptionValueDiagnostic ( "reactNamespace" , Diagnostics . Invalid_value_for_reactNamespace_0_is_not_a_valid_identifier , options . reactNamespace ) ;
2016-01-08 00:00:50 +01:00
}
2015-11-06 05:09:40 +01:00
// If the emit is enabled make sure that every output file is unique and not overwriting any of the input files
2016-02-01 19:36:47 +01:00
if ( ! options . noEmit && ! options . suppressOutputPathCheck ) {
2015-11-06 21:39:42 +01:00
const emitHost = getEmitHost ( ) ;
2017-07-10 20:24:17 +02:00
const emitFilesSeen = createMap < true > ( ) ;
2017-01-23 20:14:29 +01:00
forEachEmittedFile ( emitHost , ( emitFileNames ) = > {
2018-02-05 23:48:50 +01:00
if ( ! options . emitDeclarationOnly ) {
2018-01-26 00:35:18 +01:00
verifyEmitFilePath ( emitFileNames . jsFilePath , emitFilesSeen ) ;
}
2015-10-30 21:22:23 +01:00
verifyEmitFilePath ( emitFileNames . declarationFilePath , emitFilesSeen ) ;
} ) ;
}
2015-10-12 21:25:13 +02:00
2015-11-12 01:10:23 +01:00
// Verify that all the emit files are unique and don't overwrite input files
2017-07-10 20:24:17 +02:00
function verifyEmitFilePath ( emitFileName : string , emitFilesSeen : Map < true > ) {
2015-10-30 23:54:31 +01:00
if ( emitFileName ) {
2017-07-10 20:24:17 +02:00
const emitFilePath = toPath ( emitFileName ) ;
2015-10-12 21:25:13 +02:00
// Report error if the output overwrites input file
2017-06-28 22:15:34 +02:00
if ( filesByName . has ( emitFilePath ) ) {
2016-11-10 23:12:24 +01:00
let chain : DiagnosticMessageChain ;
if ( ! options . configFilePath ) {
// The program is from either an inferred project or an external project
chain = chainDiagnosticMessages ( /*details*/ undefined , Diagnostics . Adding_a_tsconfig_json_file_will_help_organize_projects_that_contain_both_TypeScript_and_JavaScript_files_Learn_more_at_https_Colon_Slash_Slashaka_ms_Slashtsconfig ) ;
2016-11-04 05:13:41 +01:00
}
2016-11-10 23:12:24 +01:00
chain = chainDiagnosticMessages ( chain , Diagnostics . Cannot_write_file_0_because_it_would_overwrite_input_file , emitFileName ) ;
blockEmittingOfFile ( emitFileName , createCompilerDiagnosticFromMessageChain ( chain ) ) ;
2015-10-12 21:25:13 +02:00
}
2017-07-10 20:24:17 +02:00
const emitFileKey = ! host . useCaseSensitiveFileNames ( ) ? emitFilePath . toLocaleLowerCase ( ) : emitFilePath ;
2015-10-30 21:22:23 +01:00
// Report error if multiple files write into same file
2017-07-10 20:24:17 +02:00
if ( emitFilesSeen . has ( emitFileKey ) ) {
2015-10-30 21:22:23 +01:00
// Already seen the same emit file - report error
2016-11-04 05:13:41 +01:00
blockEmittingOfFile ( emitFileName , createCompilerDiagnostic ( Diagnostics . Cannot_write_file_0_because_it_would_be_overwritten_by_multiple_input_files , emitFileName ) ) ;
2015-10-12 21:25:13 +02:00
}
else {
2017-07-10 20:24:17 +02:00
emitFilesSeen . set ( emitFileKey , true ) ;
2015-10-12 21:25:13 +02:00
}
2015-10-30 21:22:23 +01:00
}
2015-10-12 21:25:13 +02:00
}
}
2016-11-29 21:42:17 +01:00
function createDiagnosticForOptionPathKeyValue ( key : string , valueIndex : number , message : DiagnosticMessage , arg0 : string | number , arg1 : string | number , arg2? : string | number ) {
let needCompilerDiagnostic = true ;
const pathsSyntax = getOptionPathsSyntax ( ) ;
for ( const pathProp of pathsSyntax ) {
if ( isObjectLiteralExpression ( pathProp . initializer ) ) {
for ( const keyProps of getPropertyAssignment ( pathProp . initializer , key ) ) {
if ( isArrayLiteralExpression ( keyProps . initializer ) &&
keyProps . initializer . elements . length > valueIndex ) {
programDiagnostics . add ( createDiagnosticForNodeInSourceFile ( options . configFile , keyProps . initializer . elements [ valueIndex ] , message , arg0 , arg1 , arg2 ) ) ;
needCompilerDiagnostic = false ;
}
}
}
}
if ( needCompilerDiagnostic ) {
programDiagnostics . add ( createCompilerDiagnostic ( message , arg0 , arg1 , arg2 ) ) ;
}
}
function createDiagnosticForOptionPaths ( onKey : boolean , key : string , message : DiagnosticMessage , arg0 : string | number ) {
let needCompilerDiagnostic = true ;
const pathsSyntax = getOptionPathsSyntax ( ) ;
for ( const pathProp of pathsSyntax ) {
if ( isObjectLiteralExpression ( pathProp . initializer ) &&
createOptionDiagnosticInObjectLiteralSyntax (
2017-04-17 22:40:49 +02:00
pathProp . initializer , onKey , key , /*key2*/ undefined ,
2016-11-29 21:42:17 +01:00
message , arg0 ) ) {
needCompilerDiagnostic = false ;
}
}
if ( needCompilerDiagnostic ) {
programDiagnostics . add ( createCompilerDiagnostic ( message , arg0 ) ) ;
}
}
function getOptionPathsSyntax() {
const compilerOptionsObjectLiteralSyntax = getCompilerOptionsObjectLiteralSyntax ( ) ;
if ( compilerOptionsObjectLiteralSyntax ) {
return getPropertyAssignment ( compilerOptionsObjectLiteralSyntax , "paths" ) ;
}
return emptyArray ;
}
function createDiagnosticForOptionName ( message : DiagnosticMessage , option1 : string , option2? : string ) {
createDiagnosticForOption ( /*onKey*/ true , option1 , option2 , message , option1 , option2 ) ;
}
function createOptionValueDiagnostic ( option1 : string , message : DiagnosticMessage , arg0 : string ) {
createDiagnosticForOption ( /*onKey*/ false , option1 , /*option2*/ undefined , message , arg0 ) ;
}
function createDiagnosticForOption ( onKey : boolean , option1 : string , option2 : string , message : DiagnosticMessage , arg0 : string | number , arg1? : string | number ) {
const compilerOptionsObjectLiteralSyntax = getCompilerOptionsObjectLiteralSyntax ( ) ;
const needCompilerDiagnostic = ! compilerOptionsObjectLiteralSyntax ||
! createOptionDiagnosticInObjectLiteralSyntax ( compilerOptionsObjectLiteralSyntax , onKey , option1 , option2 , message , arg0 , arg1 ) ;
if ( needCompilerDiagnostic ) {
programDiagnostics . add ( createCompilerDiagnostic ( message , arg0 , arg1 ) ) ;
}
}
function getCompilerOptionsObjectLiteralSyntax() {
if ( _compilerOptionsObjectLiteralSyntax === undefined ) {
_compilerOptionsObjectLiteralSyntax = null ; // tslint:disable-line:no-null-keyword
if ( options . configFile && options . configFile . jsonObject ) {
for ( const prop of getPropertyAssignment ( options . configFile . jsonObject , "compilerOptions" ) ) {
if ( isObjectLiteralExpression ( prop . initializer ) ) {
_compilerOptionsObjectLiteralSyntax = prop . initializer ;
break ;
}
}
}
}
return _compilerOptionsObjectLiteralSyntax ;
}
function createOptionDiagnosticInObjectLiteralSyntax ( objectLiteral : ObjectLiteralExpression , onKey : boolean , key1 : string , key2 : string , message : DiagnosticMessage , arg0 : string | number , arg1? : string | number ) : boolean {
const props = getPropertyAssignment ( objectLiteral , key1 , key2 ) ;
for ( const prop of props ) {
programDiagnostics . add ( createDiagnosticForNodeInSourceFile ( options . configFile , onKey ? prop.name : prop.initializer , message , arg0 , arg1 ) ) ;
}
return ! ! props . length ;
}
2016-11-10 23:12:24 +01:00
function blockEmittingOfFile ( emitFileName : string , diag : Diagnostic ) {
2017-07-10 20:24:17 +02:00
hasEmitBlockingDiagnostics . set ( toPath ( emitFileName ) , true ) ;
2016-11-10 23:12:24 +01:00
programDiagnostics . add ( diag ) ;
2014-12-16 22:14:14 +01:00
}
2018-01-11 20:52:46 +01:00
function isEmittedFile ( file : string ) {
if ( options . noEmit ) {
return false ;
}
2018-01-30 00:41:21 +01:00
// If this is source file, its not emitted file
const filePath = toPath ( file ) ;
if ( getSourceFileByPath ( filePath ) ) {
return false ;
}
// If options have --outFile or --out just check that
const out = options . outFile || options . out ;
if ( out ) {
return isSameFile ( filePath , out ) || isSameFile ( filePath , removeFileExtension ( out ) + Extension . Dts ) ;
}
// If --outDir, check if file is in that directory
if ( options . outDir ) {
return containsPath ( options . outDir , filePath , currentDirectory , ! host . useCaseSensitiveFileNames ( ) ) ;
}
if ( fileExtensionIsOneOf ( filePath , supportedJavascriptExtensions ) || fileExtensionIs ( filePath , Extension . Dts ) ) {
// Otherwise just check if sourceFile with the name exists
const filePathWithoutExtension = removeFileExtension ( filePath ) ;
return ! ! getSourceFileByPath ( combinePaths ( filePathWithoutExtension , Extension . Ts ) as Path ) ||
! ! getSourceFileByPath ( combinePaths ( filePathWithoutExtension , Extension . Tsx ) as Path ) ;
}
return false ;
2018-01-11 20:52:46 +01:00
}
function isSameFile ( file1 : string , file2 : string ) {
return comparePaths ( file1 , file2 , currentDirectory , ! host . useCaseSensitiveFileNames ( ) ) === Comparison . EqualTo ;
}
2014-12-16 22:14:14 +01:00
}
2016-10-13 22:02:22 +02:00
/* @internal */
/ * *
2016-10-27 16:19:37 +02:00
* Returns a DiagnosticMessage if we won ' t include a resolved module due to its extension .
2016-10-13 22:02:22 +02:00
* The DiagnosticMessage ' s parameters are the imported module name , and the filename it resolved to .
2016-10-27 16:19:37 +02:00
* This returns a diagnostic even if the module will be an untyped module .
2016-10-13 22:02:22 +02:00
* /
2016-10-27 20:33:01 +02:00
export function getResolutionDiagnostic ( options : CompilerOptions , { extension } : ResolvedModuleFull ) : DiagnosticMessage | undefined {
2016-10-25 21:38:59 +02:00
switch ( extension ) {
case Extension . Ts :
case Extension . Dts :
// These are always allowed.
2016-10-18 23:22:43 +02:00
return undefined ;
2016-10-25 21:38:59 +02:00
case Extension . Tsx :
2016-10-27 17:08:02 +02:00
return needJsx ( ) ;
2016-10-25 21:38:59 +02:00
case Extension . Jsx :
2016-10-27 17:08:02 +02:00
return needJsx ( ) || needAllowJs ( ) ;
2016-10-25 21:38:59 +02:00
case Extension . Js :
2016-10-27 17:08:02 +02:00
return needAllowJs ( ) ;
}
function needJsx() {
return options . jsx ? undefined : Diagnostics . Module_0_was_resolved_to_1_but_jsx_is_not_set ;
}
function needAllowJs() {
2017-10-30 21:05:54 +01:00
return options . allowJs || ! getStrictOptionValue ( options , "noImplicitAny" ) ? undefined : Diagnostics . Could_not_find_a_declaration_file_for_module_0_1_implicitly_has_an_any_type ;
2016-10-13 22:02:22 +02:00
}
}
2017-07-12 19:13:33 +02:00
function checkAllDefined ( names : string [ ] ) : string [ ] {
Debug . assert ( names . every ( name = > name !== undefined ) , "A name is undefined." , ( ) = > JSON . stringify ( names ) ) ;
return names ;
}
2017-08-26 00:14:51 +02:00
function getModuleNames ( { imports , module Augmentations } : SourceFile ) : string [ ] {
const res = imports . map ( i = > i . text ) ;
for ( const aug of module Augmentations ) {
if ( aug . kind === SyntaxKind . StringLiteral ) {
res . push ( aug . text ) ;
}
// Do nothing if it's an Identifier; we don't need to do module resolution for `declare global`.
}
return res ;
}
2015-06-17 20:08:13 +02:00
}