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 {
2018-05-22 23:46:57 +02:00
let commonPathComponents : string [ ] | undefined ;
2016-04-01 21:41:01 +02:00
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 ;
}
2018-04-30 06:31:33 +02:00
return getPathFromPathComponents ( commonPathComponents ) ;
2016-04-01 21:41:01 +02:00
}
2016-02-09 15:23:43 +01:00
interface OutputFingerprint {
hash : string ;
byteOrderMark : boolean ;
2018-07-16 10:10:32 +02:00
mtime : Date ;
2016-02-09 15:23:43 +01:00
}
2015-03-18 22:11:50 +01:00
export function createCompilerHost ( options : CompilerOptions , setParentNodes? : boolean ) : CompilerHost {
2018-08-21 22:52:04 +02:00
return createCompilerHostWorker ( options , setParentNodes ) ;
}
2018-12-19 01:12:37 +01:00
2018-08-21 22:52:04 +02:00
/*@internal*/
// TODO(shkamat): update this after reworking ts build API
export function createCompilerHostWorker ( options : CompilerOptions , setParentNodes? : boolean , system = sys ) : 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.
2018-08-17 23:05:05 +02:00
return system . useCaseSensitiveFileNames ? fileName : fileName.toLowerCase ( ) ;
2014-12-16 22:14:14 +01:00
}
2015-06-26 01:24:41 +02:00
2018-05-22 23:46:57 +02:00
function getSourceFile ( fileName : string , languageVersion : ScriptTarget , onError ? : ( message : string ) = > void ) : SourceFile | undefined {
let text : string | undefined ;
2014-12-16 22:14:14 +01:00
try {
2016-08-15 20:07:49 +02:00
performance . mark ( "beforeIORead" ) ;
2018-11-21 18:03:26 +01:00
text = compilerHost . readFile ( fileName ) ;
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-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
}
2018-08-17 23:05:05 +02:00
if ( system . 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 ) ;
2018-11-21 18:03:26 +01:00
if ( compilerHost . createDirectory ) {
compilerHost . createDirectory ( directoryPath ) ;
2018-11-20 22:26:14 +01:00
}
else {
system . 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
}
2018-08-17 23:05:05 +02:00
const hash = system . createHash ! ( data ) ; // TODO: GH#18217
const mtimeBefore = system . getModifiedTime ! ( fileName ) ; // TODO: GH#18217
2016-02-11 09:38:21 +01:00
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
2018-08-17 23:05:05 +02:00
system . writeFile ( fileName , data , writeByteOrderMark ) ;
2016-02-09 15:23:43 +01:00
2018-08-17 23:05:05 +02:00
const mtimeAfter = system . getModifiedTime ! ( fileName ) || missingFileModifiedTime ; // TODO: GH#18217
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
2018-08-17 23:05:05 +02:00
if ( isWatchSet ( options ) && system . createHash && system . getModifiedTime ) {
2016-02-11 09:38:21 +01:00
writeFileIfUpdated ( fileName , data , writeByteOrderMark ) ;
}
else {
2018-08-17 23:05:05 +02:00
system . writeFile ( fileName , data , writeByteOrderMark ) ;
2016-02-11 09:38:21 +01:00
}
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 {
2018-08-17 23:05:05 +02:00
return getDirectoryPath ( normalizePath ( system . getExecutingFilePath ( ) ) ) ;
2016-03-28 23:20:29 +02:00
}
2018-08-22 01:25:57 +02:00
const newLine = getNewLineCharacter ( options , ( ) = > system . newLine ) ;
2018-08-17 23:05:05 +02:00
const realpath = system . realpath && ( ( path : string ) = > system . realpath ! ( path ) ) ;
2018-11-21 18:03:26 +01:00
const compilerHost : CompilerHost = {
2014-12-16 22:14:14 +01:00
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 ,
2018-08-17 23:05:05 +02:00
getCurrentDirectory : memoize ( ( ) = > system . getCurrentDirectory ( ) ) ,
useCaseSensitiveFileNames : ( ) = > system . useCaseSensitiveFileNames ,
2014-12-16 22:14:14 +01:00
getCanonicalFileName ,
2015-07-14 02:44:50 +02:00
getNewLine : ( ) = > newLine ,
2018-08-17 23:05:05 +02:00
fileExists : fileName = > system . fileExists ( fileName ) ,
readFile : fileName = > system . readFile ( fileName ) ,
trace : ( s : string ) = > system . write ( s + newLine ) ,
directoryExists : directoryName = > system . directoryExists ( directoryName ) ,
getEnvironmentVariable : name = > system . getEnvironmentVariable ? system . getEnvironmentVariable ( name ) : "" ,
getDirectories : ( path : string ) = > system . getDirectories ( path ) ,
2018-05-08 00:12:50 +02:00
realpath ,
2018-11-20 22:26:14 +01:00
readDirectory : ( path , extensions , include , exclude , depth ) = > system . readDirectory ( path , extensions , include , exclude , depth ) ,
createDirectory : d = > system . createDirectory ( d )
2014-12-16 22:14:14 +01:00
} ;
2018-11-21 18:03:26 +01:00
return compilerHost ;
2014-12-16 22:14:14 +01:00
}
2019-02-23 00:32:42 +01:00
/*@internal*/
2019-01-14 21:48:22 +01:00
interface CompilerHostLikeForCache {
2018-12-19 01:12:37 +01:00
fileExists ( fileName : string ) : boolean ;
readFile ( fileName : string , encoding? : string ) : string | undefined ;
directoryExists ? ( directory : string ) : boolean ;
createDirectory ? ( directory : string ) : void ;
writeFile? : WriteFileCallback ;
}
2018-11-21 18:18:03 +01:00
/*@internal*/
2018-12-19 01:12:37 +01:00
export function changeCompilerHostLikeToUseCache (
2019-01-14 21:48:22 +01:00
host : CompilerHostLikeForCache ,
2018-11-21 18:18:03 +01:00
toPath : ( fileName : string ) = > Path ,
2018-12-19 01:12:37 +01:00
getSourceFile? : CompilerHost [ "getSourceFile" ]
2018-11-21 18:18:03 +01:00
) {
const originalReadFile = host . readFile ;
const originalFileExists = host . fileExists ;
const originalDirectoryExists = host . directoryExists ;
const originalCreateDirectory = host . createDirectory ;
const originalWriteFile = host . writeFile ;
const readFileCache = createMap < string | false > ( ) ;
const fileExistsCache = createMap < boolean > ( ) ;
const directoryExistsCache = createMap < boolean > ( ) ;
const sourceFileCache = createMap < SourceFile > ( ) ;
const readFileWithCache = ( fileName : string ) : string | undefined = > {
const key = toPath ( fileName ) ;
const value = readFileCache . get ( key ) ;
2019-01-30 21:32:08 +01:00
if ( value !== undefined ) return value !== false ? value : undefined ;
2018-11-21 18:18:03 +01:00
return setReadFileCache ( key , fileName ) ;
} ;
const setReadFileCache = ( key : Path , fileName : string ) = > {
const newValue = originalReadFile . call ( host , fileName ) ;
2019-01-30 21:32:08 +01:00
readFileCache . set ( key , newValue !== undefined ? newValue : false ) ;
2018-11-21 18:18:03 +01:00
return newValue ;
} ;
host . readFile = fileName = > {
const key = toPath ( fileName ) ;
const value = readFileCache . get ( key ) ;
2019-01-30 21:32:08 +01:00
if ( value !== undefined ) return value !== false ? value : undefined ; // could be .d.ts from output
2019-02-06 04:35:08 +01:00
// Cache json or buildInfo
2019-02-21 20:28:52 +01:00
if ( ! fileExtensionIs ( fileName , Extension . Json ) && ! isBuildInfoFile ( fileName ) ) {
2018-11-21 18:18:03 +01:00
return originalReadFile . call ( host , fileName ) ;
}
return setReadFileCache ( key , fileName ) ;
} ;
2018-12-19 01:12:37 +01:00
const getSourceFileWithCache : CompilerHost [ "getSourceFile" ] | undefined = getSourceFile ? ( fileName , languageVersion , onError , shouldCreateNewSourceFile ) = > {
const key = toPath ( fileName ) ;
const value = sourceFileCache . get ( key ) ;
if ( value ) return value ;
2018-11-21 18:18:03 +01:00
2018-12-19 01:12:37 +01:00
const sourceFile = getSourceFile ( fileName , languageVersion , onError , shouldCreateNewSourceFile ) ;
if ( sourceFile && ( isDeclarationFileName ( fileName ) || fileExtensionIs ( fileName , Extension . Json ) ) ) {
sourceFileCache . set ( key , sourceFile ) ;
}
return sourceFile ;
} : undefined ;
2018-11-21 18:18:03 +01:00
// fileExists for any kind of extension
host . fileExists = fileName = > {
const key = toPath ( fileName ) ;
const value = fileExistsCache . get ( key ) ;
if ( value !== undefined ) return value ;
const newValue = originalFileExists . call ( host , fileName ) ;
fileExistsCache . set ( key , ! ! newValue ) ;
return newValue ;
} ;
2018-12-19 01:12:37 +01:00
if ( originalWriteFile ) {
host . writeFile = ( fileName , data , writeByteOrderMark , onError , sourceFiles ) = > {
const key = toPath ( fileName ) ;
fileExistsCache . delete ( key ) ;
2018-11-21 18:18:03 +01:00
2018-12-19 01:12:37 +01:00
const value = readFileCache . get ( key ) ;
2019-02-06 04:35:08 +01:00
if ( value !== undefined && value !== data ) {
2018-12-19 01:12:37 +01:00
readFileCache . delete ( key ) ;
2018-11-21 18:18:03 +01:00
sourceFileCache . delete ( key ) ;
}
2018-12-19 01:12:37 +01:00
else if ( getSourceFileWithCache ) {
const sourceFile = sourceFileCache . get ( key ) ;
if ( sourceFile && sourceFile . text !== data ) {
sourceFileCache . delete ( key ) ;
}
}
originalWriteFile . call ( host , fileName , data , writeByteOrderMark , onError , sourceFiles ) ;
} ;
}
2018-11-21 18:18:03 +01:00
// directoryExists
if ( originalDirectoryExists && originalCreateDirectory ) {
host . directoryExists = directory = > {
const key = toPath ( directory ) ;
const value = directoryExistsCache . get ( key ) ;
if ( value !== undefined ) return value ;
const newValue = originalDirectoryExists . call ( host , directory ) ;
directoryExistsCache . set ( key , ! ! newValue ) ;
return newValue ;
} ;
host . createDirectory = directory = > {
const key = toPath ( directory ) ;
directoryExistsCache . delete ( key ) ;
originalCreateDirectory . call ( host , directory ) ;
} ;
}
return {
originalReadFile ,
originalFileExists ,
originalDirectoryExists ,
originalCreateDirectory ,
originalWriteFile ,
2018-12-19 01:12:37 +01:00
getSourceFileWithCache ,
2018-11-21 18:18:03 +01:00
readFileWithCache
} ;
}
2018-10-30 16:41:31 +01:00
export function getPreEmitDiagnostics ( program : Program , sourceFile? : SourceFile , cancellationToken? : CancellationToken ) : ReadonlyArray < Diagnostic > {
2017-08-24 18:55:01 +02:00
const diagnostics = [
2018-03-15 22:32:01 +01:00
. . . program . getConfigFileParsingDiagnostics ( ) ,
2017-08-24 18:55:01 +02:00
. . . program . getOptionsDiagnostics ( cancellationToken ) ,
. . . program . getSyntacticDiagnostics ( sourceFile , cancellationToken ) ,
. . . program . getGlobalDiagnostics ( cancellationToken ) ,
. . . program . getSemanticDiagnostics ( sourceFile , cancellationToken )
] ;
2015-03-20 00:55:07 +01:00
2018-09-18 22:16:25 +02:00
if ( getEmitDeclarations ( program . getCompilerOptions ( ) ) ) {
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 ) {
2018-05-22 23:46:57 +02:00
const { line , character } = getLineAndCharacterOfPosition ( diagnostic . file , diagnostic . start ! ) ; // TODO: GH#18217
2017-08-04 04:14:47 +02:00
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"
}
2018-09-08 22:12:09 +02:00
const gutterStyleSequence = "\u001b[7m" ;
2017-05-13 01:17:32 +02:00
const gutterSeparator = " " ;
const resetEscapeSequence = "\u001b[0m" ;
const ellipsis = "..." ;
2018-06-15 19:54:36 +02:00
const halfIndent = " " ;
const indent = " " ;
function getCategoryFormat ( category : DiagnosticCategory ) : ForegroundColorEscapeSequences {
2017-05-13 01:17:32 +02:00
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 ;
}
2018-06-15 19:54:36 +02:00
function formatCodeSpan ( file : SourceFile , start : number , length : number , indent : string , squiggleColor : ForegroundColorEscapeSequences , host : FormatDiagnosticsHost ) {
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 hasMoreThanFiveLines = ( lastLine - firstLine ) >= 4 ;
let gutterWidth = ( lastLine + 1 + "" ) . length ;
if ( hasMoreThanFiveLines ) {
gutterWidth = Math . max ( ellipsis . length , gutterWidth ) ;
}
let context = "" ;
for ( let i = firstLine ; i <= lastLine ; i ++ ) {
context += host . getNewLine ( ) ;
// 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 ) {
context += indent + formatColorAndReset ( padLeft ( ellipsis , gutterWidth ) , gutterStyleSequence ) + gutterSeparator + host . getNewLine ( ) ;
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.
context += indent + formatColorAndReset ( padLeft ( i + 1 + "" , gutterWidth ) , gutterStyleSequence ) + gutterSeparator ;
context += lineContent + host . getNewLine ( ) ;
// Output the gutter and the error span for the line using tildes.
context += indent + formatColorAndReset ( padLeft ( "" , gutterWidth ) , gutterStyleSequence ) + gutterSeparator ;
context += squiggleColor ;
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 ;
context += lineContent . slice ( 0 , firstLineChar ) . replace ( /\S/g , " " ) ;
context += lineContent . slice ( firstLineChar , lastCharForLine ) . replace ( /./g , "~" ) ;
}
else if ( i === lastLine ) {
context += lineContent . slice ( 0 , lastLineChar ) . replace ( /./g , "~" ) ;
}
else {
// Squiggle the entire line.
context += lineContent . replace ( /./g , "~" ) ;
}
context += resetEscapeSequence ;
}
return context ;
}
2018-07-02 19:47:52 +02:00
/* @internal */
export function formatLocation ( file : SourceFile , start : number , host : FormatDiagnosticsHost , color = formatColorAndReset ) {
2018-06-15 19:54:36 +02:00
const { line : firstLine , character : firstLineChar } = getLineAndCharacterOfPosition ( file , start ) ; // TODO: GH#18217
const relativeFileName = host ? convertToRelativePath ( file . fileName , host . getCurrentDirectory ( ) , fileName = > host . getCanonicalFileName ( fileName ) ) : file . fileName ;
let output = "" ;
2018-07-02 19:47:52 +02:00
output += color ( relativeFileName , ForegroundColorEscapeSequences . Cyan ) ;
2018-06-15 19:54:36 +02:00
output += ":" ;
2018-07-02 19:47:52 +02:00
output += color ( ` ${ firstLine + 1 } ` , ForegroundColorEscapeSequences . Yellow ) ;
2018-06-15 19:54:36 +02:00
output += ":" ;
2018-07-02 19:47:52 +02:00
output += color ( ` ${ firstLineChar + 1 } ` , ForegroundColorEscapeSequences . Yellow ) ;
2018-06-15 19:54:36 +02:00
return output ;
}
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 ) {
if ( diagnostic . file ) {
2018-06-15 19:54:36 +02:00
const { file , start } = diagnostic ;
output += formatLocation ( file , start ! , host ) ; // TODO: GH#18217
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 ) ) ;
2018-05-08 00:12:50 +02: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 ( ) ;
2018-06-15 19:54:36 +02:00
output += formatCodeSpan ( diagnostic . file , diagnostic . start ! , diagnostic . length ! , "" , getCategoryFormat ( diagnostic . category ) , host ) ; // TODO: GH#18217
if ( diagnostic . relatedInformation ) {
output += host . getNewLine ( ) ;
for ( const { file , start , length , messageText } of diagnostic . relatedInformation ) {
if ( file ) {
output += host . getNewLine ( ) ;
output += halfIndent + formatLocation ( file , start ! , host ) ; // TODO: GH#18217
output += formatCodeSpan ( file , start ! , length ! , indent , ForegroundColorEscapeSequences . Cyan , host ) ; // TODO: GH#18217
}
output += host . getNewLine ( ) ;
output += indent + flattenDiagnosticMessageText ( messageText , host . getNewLine ( ) ) ;
}
}
2017-06-17 03:59:40 +02:00
}
2017-09-07 21:26:23 +02:00
output += host . getNewLine ( ) ;
2017-05-13 01:17:32 +02:00
}
2018-06-10 22:23:16 +02:00
return output ;
2016-07-13 18:13:55 +02:00
}
2018-05-22 23:46:57 +02:00
export function flattenDiagnosticMessageText ( messageText : string | DiagnosticMessageChain | undefined , 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 ;
}
}
2018-10-02 00:30:06 +02:00
function loadWithLocalCache < T > ( names : string [ ] , containingFile : string , redirectedReference : ResolvedProjectReference | undefined , loader : ( name : string , containingFile : string , redirectedReference : ResolvedProjectReference | undefined ) = > T ) : T [ ] {
2016-04-01 21:41:01 +02:00
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 ) ) {
2018-05-22 23:46:57 +02:00
result = cache . get ( name ) ! ;
2016-12-12 17:42:12 +01:00
}
else {
2018-10-02 00:30:06 +02:00
cache . set ( name , result = loader ( name , containingFile , redirectedReference ) ) ;
2016-12-12 17:42:12 +01:00
}
2016-04-01 21:41:01 +02:00
resolutions . push ( result ) ;
}
return resolutions ;
}
2018-05-22 20:01:18 +02:00
interface DiagnosticCache < T extends Diagnostic > {
perFile? : Map < T [ ] > ;
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 ,
2018-05-22 23:46:57 +02:00
getSourceVersion : ( path : Path ) = > string | undefined ,
2017-08-30 20:49:58 +02:00
fileExists : ( fileName : string ) = > boolean ,
hasInvalidatedResolution : HasInvalidatedResolution ,
2017-09-26 22:34:56 +02:00
hasChangedAutomaticTypeDirectiveNames : boolean ,
2018-09-14 00:18:53 +02:00
projectReferences : ReadonlyArray < ProjectReference > | undefined
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 ;
}
2018-09-19 01:21:05 +02:00
let seenResolvedRefs : ResolvedProjectReference [ ] | undefined ;
2018-09-14 00:18:53 +02:00
// If project references dont match
2018-09-15 02:12:22 +02:00
if ( ! arrayIsEqualTo ( program . getProjectReferences ( ) , projectReferences , projectReferenceUptoDate ) ) {
2018-09-14 00:18:53 +02:00
return false ;
}
2017-07-25 01:57:49 +02:00
// 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 ;
2018-09-15 02:12:22 +02:00
function sourceFileNotUptoDate ( sourceFile : SourceFile ) {
return ! sourceFileVersionUptoDate ( sourceFile ) ||
2018-10-02 23:10:19 +02:00
hasInvalidatedResolution ( sourceFile . path ) ;
2018-09-15 02:12:22 +02:00
}
function sourceFileVersionUptoDate ( sourceFile : SourceFile ) {
2018-09-18 03:24:12 +02:00
return sourceFile . version === getSourceVersion ( sourceFile . resolvedPath ) ;
2018-09-15 02:12:22 +02:00
}
function projectReferenceUptoDate ( oldRef : ProjectReference , newRef : ProjectReference , index : number ) {
if ( ! projectReferenceIsEqualTo ( oldRef , newRef ) ) {
return false ;
}
2018-09-19 01:21:05 +02:00
return resolvedProjectReferenceUptoDate ( program ! . getResolvedProjectReferences ( ) ! [ index ] , oldRef ) ;
}
function resolvedProjectReferenceUptoDate ( oldResolvedRef : ResolvedProjectReference | undefined , oldRef : ProjectReference ) : boolean {
2018-09-15 02:12:22 +02:00
if ( oldResolvedRef ) {
2018-09-19 01:21:05 +02:00
if ( contains ( seenResolvedRefs , oldResolvedRef ) ) {
// Assume true
return true ;
}
2018-09-15 02:12:22 +02:00
// If sourceFile for the oldResolvedRef existed, check the version for uptodate
2018-09-19 01:21:05 +02:00
if ( ! sourceFileVersionUptoDate ( oldResolvedRef . sourceFile ) ) {
return false ;
}
// Add to seen before checking the referenced paths of this config file
( seenResolvedRefs || ( seenResolvedRefs = [ ] ) ) . push ( oldResolvedRef ) ;
// If child project references are upto date, this project reference is uptodate
return ! forEach ( oldResolvedRef . references , ( childResolvedRef , index ) = >
! resolvedProjectReferenceUptoDate ( childResolvedRef , oldResolvedRef . commandLine . projectReferences ! [ index ] ) ) ;
2018-09-15 02:12:22 +02:00
}
2018-09-19 01:21:05 +02:00
2018-09-15 02:12:22 +02:00
// In old program, not able to resolve project reference path,
// so if config file doesnt exist, it is uptodate.
return ! fileExists ( resolveProjectReferencePath ( oldRef ) ) ;
2017-07-25 01:57:49 +02:00
}
}
2018-03-15 22:32:01 +01:00
export function getConfigFileParsingDiagnostics ( configFileParseResult : ParsedCommandLine ) : ReadonlyArray < Diagnostic > {
return configFileParseResult . options . configFile ?
2018-05-22 20:01:18 +02:00
[ . . . configFileParseResult . options . configFile . parseDiagnostics , . . . configFileParseResult . errors ] :
2018-03-15 22:32:01 +01:00
configFileParseResult . errors ;
}
2017-08-15 00:52:20 +02:00
/ * *
2018-09-17 19:53:48 +02:00
* Determine if source file needs to be re - created even if its text hasn ' t changed
2017-08-15 00:52:20 +02:00
* /
2018-09-17 19:53:48 +02:00
function shouldProgramCreateNewSourceFiles ( program : Program | undefined , newOptions : CompilerOptions ) : boolean {
if ( ! program ) return false ;
// If any compiler options change, we can't reuse old source file even if version match
// The change in options like these could result in change in syntax tree or `sourceFile.bindDiagnostics`.
const oldOptions = program . getCompilerOptions ( ) ;
return ! ! sourceFileAffectingCompilerOptions . some ( option = >
! isJsonEqual ( getCompilerOptionValue ( oldOptions , option ) , getCompilerOptionValue ( newOptions , option ) ) ) ;
2017-07-25 01:57:49 +02:00
}
2018-05-08 00:12:50 +02:00
function createCreateProgramOptions ( rootNames : ReadonlyArray < string > , options : CompilerOptions , host? : CompilerHost , oldProgram? : Program , configFileParsingDiagnostics? : ReadonlyArray < Diagnostic > ) : CreateProgramOptions {
return {
rootNames ,
options ,
host ,
oldProgram ,
configFileParsingDiagnostics
} ;
}
2018-06-02 04:01:04 +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 createProgramOptions - The options for creating a program .
* @returns A 'Program' object .
* /
export function createProgram ( createProgramOptions : CreateProgramOptions ) : Program ;
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 .
2018-03-15 22:32:01 +01:00
* @param configFileParsingDiagnostics - error during config file parsing
2017-05-23 19:40:46 +02:00
* @returns A 'Program' object .
* /
2018-05-08 00:12:50 +02:00
export function createProgram ( rootNames : ReadonlyArray < string > , options : CompilerOptions , host? : CompilerHost , oldProgram? : Program , configFileParsingDiagnostics? : ReadonlyArray < Diagnostic > ) : Program ;
export function createProgram ( rootNamesOrOptions : ReadonlyArray < string > | CreateProgramOptions , _options? : CompilerOptions , _host? : CompilerHost , _oldProgram? : Program , _configFileParsingDiagnostics? : ReadonlyArray < Diagnostic > ) : Program {
2018-05-22 23:46:57 +02:00
const createProgramOptions = isArray ( rootNamesOrOptions ) ? createCreateProgramOptions ( rootNamesOrOptions , _options ! , _host , _oldProgram , _configFileParsingDiagnostics ) : rootNamesOrOptions ; // TODO: GH#18217
2018-05-08 00:12:50 +02:00
const { rootNames , options , configFileParsingDiagnostics , projectReferences } = createProgramOptions ;
2018-05-22 23:46:57 +02:00
let { oldProgram } = createProgramOptions ;
2018-05-08 00:12:50 +02:00
2015-03-13 23:03:17 +01:00
let program : Program ;
2018-05-05 02:23:56 +02:00
let processingDefaultLibFiles : SourceFile [ ] | undefined ;
let processingOtherFiles : SourceFile [ ] | undefined ;
2018-05-29 22:46:32 +02:00
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 > ;
2018-10-21 19:57:23 +02:00
const ambientModuleNameToUnmodifiedFileName = createMap < string > ( ) ;
2014-12-16 22:14:14 +01:00
2018-05-22 20:01:18 +02:00
const cachedSemanticDiagnosticsForFile : DiagnosticCache < Diagnostic > = { } ;
const cachedDeclarationDiagnosticsForFile : DiagnosticCache < DiagnosticWithLocation > = { } ;
2017-03-07 22:26:41 +01:00
2018-10-22 21:12:12 +02:00
let resolvedTypeReferenceDirectives = createMap < ResolvedTypeReferenceDirective | undefined > ( ) ;
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
2018-05-22 23:46:57 +02:00
const host = createProgramOptions . host || createCompilerHost ( options ) ;
2018-12-19 01:12:37 +01:00
const configParsingHost = parseConfigHostFromCompilerHostLike ( host ) ;
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 ) ;
2018-10-11 23:19:32 +02:00
const supportedExtensionsWithJsonIfResolveJsonModule = getSuppoertedExtensionsWithJsonIfResolveJsonModule ( options , supportedExtensions ) ;
2016-04-11 05:42:22 +02:00
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 > ( ) ;
2018-05-22 23:46:57 +02:00
let _compilerOptionsObjectLiteralSyntax : ObjectLiteralExpression | null | undefined ;
2015-10-01 01:10:52 +02:00
2018-05-22 23:46:57 +02:00
let module ResolutionCache : ModuleResolutionCache | undefined ;
2018-10-02 00:30:06 +02:00
let resolveModuleNamesWorker : ( module Names : string [ ] , containingFile : string , reusedNames? : string [ ] , redirectedReference? : ResolvedProjectReference ) = > 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 ) {
2018-10-02 00:30:06 +02:00
resolveModuleNamesWorker = ( module Names , containingFile , reusedNames , redirectedReference ) = > host . resolveModuleNames ! ( Debug . assertEachDefined ( module Names ) , containingFile , reusedNames , redirectedReference ) . 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 ) ) ;
2018-10-02 00:30:06 +02:00
const loader = ( module Name : string , containingFile : string , redirectedReference : ResolvedProjectReference | undefined ) = > resolveModuleName ( module Name , containingFile , options , host , module ResolutionCache , redirectedReference ) . resolvedModule ! ; // TODO: GH#18217
resolveModuleNamesWorker = ( module Names , containingFile , _reusedNames , redirectedReference ) = > loadWithLocalCache < ResolvedModuleFull > ( Debug . assertEachDefined ( module Names ) , containingFile , redirectedReference , loader ) ;
2016-04-01 21:41:01 +02:00
}
2018-10-22 21:12:12 +02:00
let resolveTypeReferenceDirectiveNamesWorker : ( typeDirectiveNames : string [ ] , containingFile : string , redirectedReference? : ResolvedProjectReference ) = > ( ResolvedTypeReferenceDirective | undefined ) [ ] ;
2016-04-01 21:41:01 +02:00
if ( host . resolveTypeReferenceDirectives ) {
2018-10-02 00:30:06 +02:00
resolveTypeReferenceDirectiveNamesWorker = ( typeDirectiveNames , containingFile , redirectedReference ) = > host . resolveTypeReferenceDirectives ! ( Debug . assertEachDefined ( typeDirectiveNames ) , containingFile , redirectedReference ) ;
2016-04-01 21:41:01 +02:00
}
else {
2018-10-02 00:30:06 +02:00
const loader = ( typesRef : string , containingFile : string , redirectedReference : ResolvedProjectReference | undefined ) = > resolveTypeReferenceDirective ( typesRef , containingFile , options , host , redirectedReference ) . resolvedTypeReferenceDirective ! ; // TODO: GH#18217
resolveTypeReferenceDirectiveNamesWorker = ( typeReferenceDirectiveNames , containingFile , redirectedReference ) = > loadWithLocalCache < ResolvedTypeReferenceDirective > ( Debug . assertEachDefined ( typeReferenceDirectiveNames ) , containingFile , redirectedReference , 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 > ( ) ;
2018-08-01 02:28:56 +02:00
// Key is a file name. Value is the (non-empty, or undefined) list of files that redirect to it.
let redirectTargetsMap = createMultiMap < string > ( ) ;
2017-08-09 23:39:06 +02:00
2018-12-06 00:31:55 +01:00
/ * *
* map with
* - SourceFile if present
* - false if sourceFile missing for source of project reference redirect
* - undefined otherwise
* /
const filesByName = createMap < SourceFile | false | undefined > ( ) ;
2018-05-22 23:46:57 +02:00
let missingFilePaths : ReadonlyArray < Path > | undefined ;
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
2018-05-08 00:12:50 +02:00
// A parallel array to projectReferences storing the results of reading in the referenced tsconfig files
2018-09-19 01:21:05 +02:00
let resolvedProjectReferences : ReadonlyArray < ResolvedProjectReference | undefined > | undefined ;
let projectReferenceRedirects : Map < ResolvedProjectReference | false > | undefined ;
2018-11-19 21:58:49 +01:00
let mapFromFileToProjectReferenceRedirects : Map < Path > | undefined ;
2018-06-06 01:28:42 +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 ) {
2018-05-05 02:23:56 +02:00
processingDefaultLibFiles = [ ] ;
processingOtherFiles = [ ] ;
2018-06-06 01:20:07 +02:00
if ( projectReferences ) {
2018-09-19 01:21:05 +02:00
if ( ! resolvedProjectReferences ) {
resolvedProjectReferences = projectReferences . map ( parseProjectReferenceConfigFile ) ;
}
2018-10-31 23:03:42 +01:00
if ( rootNames . length ) {
for ( const parsedRef of resolvedProjectReferences ) {
2019-03-08 19:19:02 +01:00
if ( ! parsedRef ) continue ;
const out = parsedRef . commandLine . options . outFile || parsedRef . commandLine . options . out ;
if ( out ) {
processSourceFile ( changeExtension ( out , ".d.ts" ) , /*isDefaultLib*/ false , /*ignoreNoDefaultLib*/ false , /*packageId*/ undefined ) ;
}
else if ( getEmitModuleKind ( parsedRef . commandLine . options ) === ModuleKind . None ) {
for ( const fileName of parsedRef . commandLine . fileNames ) {
if ( ! fileExtensionIs ( fileName , Extension . Dts ) && hasTSFileExtension ( fileName ) ) {
2019-03-13 17:58:26 +01:00
processSourceFile ( getOutputDeclarationFileName ( fileName , parsedRef . commandLine , ! host . useCaseSensitiveFileNames ( ) ) , /*isDefaultLib*/ false , /*ignoreNoDefaultLib*/ false , /*packageId*/ undefined ) ;
2019-03-08 19:19:02 +01:00
}
2018-10-31 23:03:42 +01:00
}
2018-06-06 01:20:07 +02:00
}
}
}
}
2018-05-03 20:00:10 +02:00
forEach ( rootNames , name = > processRootFile ( name , /*isDefaultLib*/ false , /*ignoreNoDefaultLib*/ false ) ) ;
2016-05-18 20:30:40 +02:00
2016-06-11 00:44:11 +02:00
// load type declarations specified via 'types' argument or implicitly from types/ and node_modules/@types folders
2018-10-31 23:03:42 +01:00
const typeReferences : string [ ] = rootNames . length ? getAutomaticTypeDirectiveNames ( options , host ) : emptyArray ;
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.
2018-11-01 03:00:55 +01:00
if ( rootNames . length && ! 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
2018-04-02 20:58:37 +02:00
const defaultLibraryFileName = getDefaultLibraryFileName ( ) ;
if ( ! options . lib && defaultLibraryFileName ) {
2018-05-03 20:00:10 +02:00
processRootFile ( defaultLibraryFileName , /*isDefaultLib*/ true , /*ignoreNoDefaultLib*/ false ) ;
2016-03-28 23:20:29 +02:00
}
else {
2016-03-29 01:24:16 +02:00
forEach ( options . lib , libFileName = > {
2018-05-03 20:00:10 +02:00
processRootFile ( combinePaths ( defaultLibraryPath , libFileName ) , /*isDefaultLib*/ true , /*ignoreNoDefaultLib*/ false ) ;
2016-03-28 23:20:29 +02:00
} ) ;
}
2015-06-23 02:48:44 +02:00
}
2015-05-01 03:14:53 +02:00
2018-12-06 00:31:55 +01:00
missingFilePaths = arrayFrom ( mapDefinedIterator ( filesByName . entries ( ) , ( [ path , file ] ) = > file === undefined ? path as Path : undefined ) ) ;
2018-05-05 02:23:56 +02:00
files = stableSort ( processingDefaultLibFiles , compareDefaultLibFiles ) . concat ( processingOtherFiles ) ;
processingDefaultLibFiles = undefined ;
processingOtherFiles = undefined ;
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
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 ) {
2018-10-02 23:10:19 +02:00
const newFile = getSourceFileByPath ( oldSourceFile . resolvedPath ) ;
if ( shouldCreateNewSourceFile || ! newFile ||
// old file wasnt redirect but new file is
( oldSourceFile . resolvedPath === oldSourceFile . path && newFile . resolvedPath !== oldSourceFile . path ) ) {
host . onReleaseOldSourceFile ( oldSourceFile , oldProgram . getCompilerOptions ( ) , ! ! getSourceFileByPath ( oldSourceFile . path ) ) ;
2017-07-25 01:57:49 +02:00
}
}
2018-10-02 23:10:19 +02:00
oldProgram . forEachResolvedProjectReference ( ( resolvedProjectReference , resolvedProjectReferencePath ) = > {
if ( resolvedProjectReference && ! getResolvedProjectReferenceByPath ( resolvedProjectReferencePath ) ) {
host . onReleaseOldSourceFile ! ( resolvedProjectReference . sourceFile , oldProgram ! . getCompilerOptions ( ) , /*hasSourceFileByPath*/ false ) ;
}
} ) ;
2017-07-25 01:57:49 +02:00
}
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 ,
2018-05-22 23:46:57 +02:00
getMissingFilePaths : ( ) = > missingFilePaths ! , // TODO: GH#18217
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 ,
2018-05-31 19:47:17 +02:00
getSuggestionDiagnostics ,
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 ,
2018-05-31 18:50:51 +02:00
getLibFileFromReference ,
2017-08-09 23:39:06 +02:00
sourceFileToPackageName ,
2018-08-01 02:28:56 +02:00
redirectTargetsMap ,
2018-03-15 22:32:01 +01:00
isEmittedFile ,
2018-04-20 22:43:09 +02:00
getConfigFileParsingDiagnostics ,
getResolvedModuleWithFailedLookupLocationsFromCache ,
2018-09-14 00:18:53 +02:00
getProjectReferences ,
2018-09-14 22:23:07 +02:00
getResolvedProjectReferences ,
2018-10-02 23:10:19 +02:00
getProjectReferenceRedirect ,
getResolvedProjectReferenceToRedirect ,
getResolvedProjectReferenceByPath ,
2019-02-06 04:35:08 +01:00
forEachResolvedProjectReference ,
emitBuildInfo
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 ;
2018-05-05 02:23:56 +02:00
function compareDefaultLibFiles ( a : SourceFile , b : SourceFile ) {
return compareValues ( getDefaultLibFilePriority ( a ) , getDefaultLibFilePriority ( b ) ) ;
}
function getDefaultLibFilePriority ( a : SourceFile ) {
if ( containsPath ( defaultLibraryPath , a . fileName , /*ignoreCase*/ false ) ) {
const basename = getBaseFileName ( a . fileName ) ;
if ( basename === "lib.d.ts" || basename === "lib.es6.d.ts" ) return 0 ;
const name = removeSuffix ( removePrefix ( basename , "lib." ) , ".d.ts" ) ;
const index = libs . indexOf ( name ) ;
if ( index !== - 1 ) return index + 1 ;
}
return libs . length + 2 ;
}
2018-05-22 23:46:57 +02:00
function getResolvedModuleWithFailedLookupLocationsFromCache ( module Name : string , containingFile : string ) : ResolvedModuleWithFailedLookupLocations | undefined {
2018-04-20 22:43:09 +02:00
return module ResolutionCache && resolveModuleNameFromCache ( module Name , containingFile , module ResolutionCache ) ;
}
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 ) {
2019-03-14 18:58:57 +01:00
const emittedFiles = filter ( files , file = > sourceFileMayBeEmitted ( file , options , isSourceFileFromExternalLibrary , getResolvedProjectReferenceToRedirect ) ) ;
2016-11-02 15:05:54 +01:00
if ( options . rootDir && checkSourceFilesBelongToPath ( emittedFiles , options . rootDir ) ) {
2018-05-08 00:12:50 +02:00
// If a rootDir is specified use it as the commonSourceDirectory
2015-11-19 02:10:22 +01:00
commonSourceDirectory = getNormalizedAbsolutePath ( options . rootDir , currentDirectory ) ;
}
2018-09-18 22:16:25 +02:00
else if ( options . composite && options . configFilePath ) {
2018-05-08 00:12:50 +02:00
// Project compilations never infer their root from the input source paths
2018-09-18 22:16:25 +02:00
commonSourceDirectory = getDirectoryPath ( normalizeSlashes ( options . configFilePath ) ) ;
2018-05-08 00:12:50 +02:00
checkSourceFilesBelongToPath ( emittedFiles , commonSourceDirectory ) ;
}
2015-11-19 02:10:22 +01:00
else {
2016-11-02 15:05:54 +01:00
commonSourceDirectory = computeCommonSourceDirectory ( emittedFiles ) ;
2015-11-19 02:10:22 +01:00
}
2018-05-08 00:12:50 +02:00
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 ) {
2018-05-22 23:46:57 +02: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
}
2018-10-21 18:15:01 +02:00
function resolveModuleNamesReusingOldState ( module Names : string [ ] , containingFile : string , file : SourceFile ) {
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.
2018-10-02 23:10:19 +02:00
return resolveModuleNamesWorker ( module Names , containingFile , /*reusedNames*/ undefined , getResolvedProjectReferenceToRedirect ( file . originalFileName ) ) ;
2016-11-02 22:41:26 +01:00
}
2018-10-21 18:15:01 +02:00
const oldSourceFile = oldProgram && oldProgram . getSourceFile ( containingFile ) ;
2017-04-29 01:27:41 +02:00
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,
2018-05-08 23:36:32 +02:00
// which per above occurred 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 ) {
2018-05-22 23:46:57 +02:00
const resolvedModule = file . resolvedModules . get ( module Name ) ! ;
2017-04-29 01:27:41 +02:00
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. */
2018-05-22 23:46:57 +02:00
let unknownModuleNames : string [ ] | undefined ;
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 .
* /
2018-05-22 23:46:57 +02:00
let result : ResolvedModuleFull [ ] | undefined ;
let reusedNames : string [ ] | undefined ;
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 ) ) {
2018-05-22 23:46:57 +02:00
const oldResolvedModule = oldSourceFile && oldSourceFile . resolvedModules ! . get ( module Name ) ;
2017-04-28 05:27:48 +02:00
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 {
2018-10-21 18:15:01 +02:00
resolvesToAmbientModuleInNonModifiedFile = module NameResolvesToAmbientModuleInNonModifiedFile ( module Name ) ;
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
2018-10-02 23:10:19 +02:00
? resolveModuleNamesWorker ( unknownModuleNames , containingFile , reusedNames , getResolvedProjectReferenceToRedirect ( file . originalFileName ) )
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 ) {
2018-05-22 23:46:57 +02:00
result [ i ] = undefined ! ; // TODO: GH#18217
2017-04-28 05:27:48 +02:00
}
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.
2018-10-21 18:15:01 +02:00
function module NameResolvesToAmbientModuleInNonModifiedFile ( module Name : string ) : boolean {
const resolutionToFile = getResolvedModule ( oldSourceFile ! , module Name ) ;
const resolvedFile = resolutionToFile && oldProgram ! . getSourceFile ( resolutionToFile . resolvedFileName ) ;
2017-12-06 22:17:21 +01:00
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 ;
}
// at least one of declarations should come from non-modified source file
2018-10-21 19:57:23 +02:00
const unmodifiedFile = ambientModuleNameToUnmodifiedFileName . get ( module Name ) ;
2016-11-02 22:41:26 +01:00
2018-10-21 19:57:23 +02:00
if ( ! unmodifiedFile ) {
2016-11-02 22:41:26 +01:00
return false ;
}
if ( isTraceEnabled ( options , host ) ) {
2018-10-21 19:57:23 +02:00
trace ( host , Diagnostics . Module_0_was_resolved_as_ambient_module_declared_in_1_since_this_file_was_not_modified , module Name , unmodifiedFile ) ;
2016-11-02 22:41:26 +01:00
}
return true ;
}
}
2018-10-02 23:10:19 +02:00
function canReuseProjectReferences ( ) : boolean {
return ! forEachProjectReference (
oldProgram ! . getProjectReferences ( ) ,
oldProgram ! . getResolvedProjectReferences ( ) ,
( oldResolvedRef , index , parent ) = > {
const newRef = ( parent ? parent.commandLine.projectReferences : projectReferences ) ! [ index ] ;
const newResolvedRef = parseProjectReferenceConfigFile ( newRef ) ;
if ( oldResolvedRef ) {
// Resolved project reference has gone missing or changed
return ! newResolvedRef || newResolvedRef . sourceFile !== oldResolvedRef . sourceFile ;
2018-09-19 01:21:05 +02:00
}
else {
// A previously-unresolved reference may be resolved now
2018-10-02 23:10:19 +02:00
return newResolvedRef !== undefined ;
2018-09-19 01:21:05 +02:00
}
2018-10-02 23:10:19 +02:00
} ,
( oldProjectReferences , parent ) = > {
// If array of references is changed, we cant resue old program
const newReferences = parent ? getResolvedProjectReferenceByPath ( parent . sourceFile . path ) ! . commandLine.projectReferences : projectReferences ;
return ! arrayIsEqualTo ( oldProjectReferences , newReferences , projectReferenceIsEqualTo ) ;
2018-09-19 01:21:05 +02:00
}
2018-10-02 23:10:19 +02:00
) ;
2018-09-19 01:21:05 +02:00
}
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
}
2018-05-22 23:46:57 +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
}
2018-05-08 00:12:50 +02:00
// Check if any referenced project tsconfig files are different
2018-10-02 23:10:19 +02:00
if ( ! canReuseProjectReferences ( ) ) {
2018-09-14 00:18:53 +02:00
return oldProgram . structureIsReused = StructureIsReused . Not ;
}
2018-10-04 01:09:16 +02:00
if ( projectReferences ) {
resolvedProjectReferences = projectReferences . map ( parseProjectReferenceConfigFile ) ;
}
2018-05-08 00:12:50 +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 [ ] = [ ] ;
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
2018-09-18 03:24:12 +02:00
? host . getSourceFileByPath ( oldSourceFile . fileName , oldSourceFile . resolvedPath , options . target ! , /*onError*/ undefined , shouldCreateNewSourceFile )
2018-05-22 23:46:57 +02:00
: host . getSourceFile ( oldSourceFile . fileName , options . target ! , /*onError*/ undefined , shouldCreateNewSourceFile ) ; // TODO: GH#18217
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.
}
2018-08-01 02:28:56 +02:00
else if ( oldProgram . redirectTargetsMap . has ( oldSourceFile . path ) ) {
2017-08-09 23:39:06 +02:00
// 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 ;
}
2018-09-18 03:24:12 +02:00
// Since the project references havent changed, its right to set originalFileName and resolvedPath here
2015-10-29 22:54:56 +01:00
newSourceFile . path = oldSourceFile . path ;
2018-09-18 03:24:12 +02:00
newSourceFile . originalFileName = oldSourceFile . originalFileName ;
newSourceFile . resolvedPath = oldSourceFile . resolvedPath ;
newSourceFile . fileName = oldSourceFile . fileName ;
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.
2018-05-03 20:00:10 +02:00
if ( ! arrayIsEqualTo ( oldSourceFile . libReferenceDirectives , newSourceFile . libReferenceDirectives , fileReferenceIsEqualTo ) ) {
2018-05-05 00:50:14 +02:00
// 'lib' references has changed. Matches behavior in changesAffectModuleResolution
2018-05-03 20:00:10 +02:00
return oldProgram . structureIsReused = StructureIsReused . Not ;
}
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
}
2018-04-10 02:03:05 +02:00
if ( ( oldSourceFile . flags & NodeFlags . PermanentlySetIncrementalFlags ) !== ( newSourceFile . flags & NodeFlags . PermanentlySetIncrementalFlags ) ) {
2017-06-15 02:13:35 +02:00
// 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
2018-10-21 18:15:01 +02:00
const modifiedFiles = modifiedSourceFiles . map ( f = > f . oldFile ) ;
2018-10-21 19:57:23 +02:00
for ( const oldFile of oldSourceFiles ) {
if ( ! contains ( modifiedFiles , oldFile ) ) {
for ( const module Name of oldFile . ambientModuleNames ) {
ambientModuleNameToUnmodifiedFileName . set ( module Name , oldFile . fileName ) ;
}
}
}
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 ) {
2018-09-18 03:24:12 +02:00
const newSourceFilePath = getNormalizedAbsolutePath ( newSourceFile . originalFileName , currentDirectory ) ;
2016-11-02 22:41:26 +01:00
if ( resolveModuleNamesWorker ) {
2017-08-26 00:14:51 +02:00
const module Names = getModuleNames ( newSourceFile ) ;
2018-10-21 18:15:01 +02:00
const resolutions = resolveModuleNamesReusingOldState ( module Names , newSourceFilePath , newSourceFile ) ;
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 ) {
2018-09-06 22:26:44 +02:00
// We lower-case all type references because npm automatically lowercases all packages. See GH#9824.
const typesReferenceDirectives = map ( newSourceFile . typeReferenceDirectives , ref = > ref . fileName . toLocaleLowerCase ( ) ) ;
2018-10-02 23:10:19 +02:00
const resolutions = resolveTypeReferenceDirectiveNamesWorker ( typesReferenceDirectives , newSourceFilePath , getResolvedProjectReferenceToRedirect ( newSourceFile . originalFileName ) ) ;
2016-11-02 22:41:26 +01:00
// 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
2018-10-02 22:11:12 +02:00
for ( const newSourceFile of newSourceFiles ) {
const filePath = newSourceFile . path ;
addFileToFilesByName ( newSourceFile , filePath , newSourceFile . resolvedPath ) ;
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,
2018-10-02 22:11:12 +02:00
if ( oldProgram . isSourceFileFromExternalLibrary ( oldProgram . getSourceFileByPath ( filePath ) ! ) ) {
sourceFilesFoundSearchingNodeModules . set ( filePath , true ) ;
2017-10-24 04:05:38 +02:00
}
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 ;
2018-08-01 02:28:56 +02:00
redirectTargetsMap = oldProgram . redirectTargetsMap ;
2017-08-09 23:39:06 +02:00
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 {
2018-05-08 00:12:50 +02:00
getPrependNodes ,
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 ,
2018-08-21 01:02:19 +02:00
getLibFileFromReference : program.getLibFileFromReference ,
2016-11-02 15:05:54 +01:00
isSourceFileFromExternalLibrary ,
2019-03-14 18:58:57 +01:00
getResolvedProjectReferenceToRedirect ,
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 ,
2018-06-05 21:54:36 +02:00
readFile : f = > host . readFile ( f ) ,
2018-06-22 23:05:36 +02:00
fileExists : f = > {
// Use local caches
const path = toPath ( f ) ;
if ( getSourceFileByPath ( path ) ) return true ;
if ( contains ( missingFilePaths , path ) ) return false ;
// Before falling back to the host
return host . fileExists ( f ) ;
} ,
2018-06-05 21:54:36 +02:00
. . . ( host . directoryExists ? { directoryExists : f = > host . directoryExists ! ( f ) } : { } ) ,
2018-06-29 20:02:43 +02:00
useCaseSensitiveFileNames : ( ) = > host . useCaseSensitiveFileNames ( ) ,
2019-02-06 04:35:08 +01:00
getProgramBuildInfo : ( ) = > program . getProgramBuildInfo && program . getProgramBuildInfo ( )
2015-02-05 01:53:14 +01:00
} ;
2014-12-16 23:42:58 +01:00
}
2019-02-06 04:35:08 +01:00
function emitBuildInfo ( writeFileCallback? : WriteFileCallback ) : EmitResult {
Debug . assert ( ! options . out && ! options . outFile ) ;
performance . mark ( "beforeEmit" ) ;
const emitResult = emitFiles (
notImplementedResolver ,
getEmitHost ( writeFileCallback ) ,
/*targetSourceFile*/ undefined ,
/*emitOnlyDtsFiles*/ false ,
/*transformers*/ undefined ,
/*declaraitonTransformers*/ undefined ,
/*onlyBuildInfo*/ true
) ;
performance . mark ( "afterEmit" ) ;
performance . measure ( "Emit" , "beforeEmit" , "afterEmit" ) ;
return emitResult ;
}
2018-09-14 00:18:53 +02:00
function getResolvedProjectReferences() {
2018-05-08 00:12:50 +02:00
return resolvedProjectReferences ;
}
2018-09-14 00:18:53 +02:00
function getProjectReferences() {
return projectReferences ;
}
2019-02-01 23:46:58 +01:00
function getPrependNodes() {
return createPrependNodes (
projectReferences ,
( _ref , index ) = > resolvedProjectReferences ! [ index ] ! . commandLine ,
fileName = > {
const path = toPath ( fileName ) ;
const sourceFile = getSourceFileByPath ( path ) ;
return sourceFile ? sourceFile.text : filesByName.has ( path ) ? undefined : host . readFile ( path ) ;
2018-05-08 00:12:50 +02:00
}
2019-02-01 23:46:58 +01:00
) ;
2018-05-08 00:12:50 +02:00
}
2016-11-08 06:13:11 +01:00
function isSourceFileFromExternalLibrary ( file : SourceFile ) : boolean {
2018-05-22 23:46:57 +02: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 ) {
2018-04-02 20:58:37 +02:00
return equalityComparer ( file . fileName , getDefaultLibraryFileName ( ) ) ;
2017-09-27 02:29:53 +02:00
}
else {
2018-05-22 23:46:57 +02:00
return some ( 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() {
2018-05-22 23:46:57 +02:00
diagnosticsProducingTypeChecker = undefined ! ;
2016-06-18 01:22:03 +02:00
}
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
}
2018-05-22 23:46:57 +02:00
function emitWorker ( program : Program , sourceFile : SourceFile | undefined , writeFileCallback : WriteFileCallback | undefined , cancellationToken : CancellationToken | undefined , 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 )
] ;
2018-09-18 22:16:25 +02:00
if ( diagnostics . length === 0 && getEmitDeclarations ( program . getCompilerOptions ( ) ) ) {
2017-11-23 03:34:49 +01:00
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 ) ,
2019-02-01 23:46:58 +01:00
sourceFile ,
2017-02-07 23:36:15 +01:00
emitOnlyDtsFiles ,
2018-05-08 20:20:48 +02:00
transformers ,
customTransformers && customTransformers . afterDeclarations
) ;
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 {
2018-12-06 00:31:55 +01:00
return filesByName . get ( path ) || undefined ;
2014-12-16 22:14:14 +01:00
}
2018-05-22 20:01:18 +02:00
function getDiagnosticsHelper < T extends Diagnostic > (
2016-02-23 21:48:31 +01:00
sourceFile : SourceFile ,
2018-05-22 20:01:18 +02:00
getDiagnostics : ( sourceFile : SourceFile , cancellationToken : CancellationToken ) = > ReadonlyArray < T > ,
cancellationToken : CancellationToken ) : ReadonlyArray < T > {
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
}
2018-05-22 20:01:18 +02:00
function getSyntacticDiagnostics ( sourceFile : SourceFile , cancellationToken : CancellationToken ) : ReadonlyArray < DiagnosticWithLocation > {
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
2018-05-22 20:01:18 +02:00
function getDeclarationDiagnostics ( sourceFile : SourceFile , cancellationToken : CancellationToken ) : ReadonlyArray < DiagnosticWithLocation > {
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
}
2018-05-22 20:01:18 +02:00
function getSyntacticDiagnosticsForFile ( sourceFile : SourceFile ) : ReadonlyArray < DiagnosticWithLocation > {
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.
2018-09-12 19:44:46 +02:00
if ( isSourceFileJS ( sourceFile ) ) {
2016-10-28 01:38:59 +02:00
if ( ! sourceFile . additionalSyntacticDiagnostics ) {
2018-09-14 00:05:57 +02:00
sourceFile . additionalSyntacticDiagnostics = getJSSyntacticDiagnosticsForFile ( sourceFile ) ;
2016-10-28 01:38:59 +02:00
}
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.
2018-05-22 23:46:57 +02:00
noDiagnosticsTypeChecker = undefined ! ;
diagnosticsProducingTypeChecker = undefined ! ;
2015-06-18 19:52:19 +02:00
}
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
2018-05-22 20:01:18 +02:00
function getSemanticDiagnosticsForFile ( sourceFile : SourceFile , cancellationToken : CancellationToken ) : ReadonlyArray < Diagnostic > {
2017-03-07 22:26:41 +01:00
return getAndCacheDiagnostics ( sourceFile , cancellationToken , cachedSemanticDiagnosticsForFile , getSemanticDiagnosticsForFileNoCache ) ;
}
2018-05-22 23:46:57 +02:00
function getSemanticDiagnosticsForFileNoCache ( sourceFile : SourceFile , cancellationToken : CancellationToken ) : Diagnostic [ ] | undefined {
2015-06-18 19:52:19 +02:00
return runWithCancellationToken ( ( ) = > {
2018-08-08 02:57:47 +02:00
if ( skipTypeChecking ( sourceFile , options ) ) {
2017-04-19 01:19:58 +02:00
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 ) ;
2018-04-06 02:30:04 +02:00
// By default, only type-check .ts, .tsx, 'Deferred' and 'External' files (external files are added by plugins)
2017-10-16 22:38:28 +02:00
const includeBindAndCheckDiagnostics = sourceFile . scriptKind === ScriptKind . TS || sourceFile . scriptKind === ScriptKind . TSX ||
2018-04-06 02:30:04 +02:00
sourceFile . scriptKind === ScriptKind . External || isCheckJs || sourceFile . scriptKind === ScriptKind . Deferred ;
2018-05-22 20:01:18 +02:00
const bindDiagnostics : ReadonlyArray < Diagnostic > = includeBindAndCheckDiagnostics ? sourceFile.bindDiagnostics : emptyArray ;
2017-06-02 04:05:07 +02:00
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 ) ;
2018-05-22 20:01:18 +02:00
let diagnostics : Diagnostic [ ] | undefined ;
for ( const diags of [ bindDiagnostics , checkDiagnostics , fileProcessingDiagnosticsInFile , programDiagnosticsInFile , isCheckJs ? sourceFile.jsDocDiagnostics : undefined ] ) {
if ( diags ) {
for ( const diag of diags ) {
if ( shouldReportDiagnostic ( diag ) ) {
diagnostics = append ( diagnostics , diag ) ;
}
}
}
2017-11-17 19:38:30 +01:00
}
2018-05-22 20:01:18 +02:00
return diagnostics ;
2015-06-18 19:52:19 +02:00
} ) ;
2014-12-16 22:14:14 +01:00
}
2018-05-31 19:47:17 +02:00
function getSuggestionDiagnostics ( sourceFile : SourceFile , cancellationToken : CancellationToken ) : ReadonlyArray < DiagnosticWithLocation > {
return runWithCancellationToken ( ( ) = > {
return getDiagnosticsProducingTypeChecker ( ) . getSuggestionDiagnostics ( sourceFile , cancellationToken ) ;
} ) ;
}
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 ) ;
2018-05-22 23:46:57 +02:00
let { line } = computeLineAndCharacterOfPosition ( lineStarts , start ! ) ; // TODO: GH#18217
2017-04-17 22:15:06 +02:00
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 ;
}
2018-09-14 00:05:57 +02:00
function getJSSyntacticDiagnosticsForFile ( sourceFile : SourceFile ) : DiagnosticWithLocation [ ] {
2015-09-09 23:41:19 +02:00
return runWithCancellationToken ( ( ) = > {
2018-05-22 20:01:18 +02:00
const diagnostics : DiagnosticWithLocation [ ] = [ ] ;
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 :
2018-03-22 23:07:36 +01:00
case SyntaxKind . JsxSelfClosingElement :
case SyntaxKind . JsxOpeningElement :
2016-11-01 20:54:27 +01:00
// Check type arguments
2018-03-22 23:07:36 +01:00
if ( nodes === ( < CallExpression | NewExpression | ExpressionWithTypeArguments | JsxOpeningLikeElement > parent ) . typeArguments ) {
2016-11-01 20:54:27 +01:00
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
}
}
}
2018-05-22 20:01:18 +02:00
function createDiagnosticForNodeArray ( nodes : NodeArray < Node > , message : DiagnosticMessage , arg0? : string | number , arg1? : string | number , arg2? : string | number ) : DiagnosticWithLocation {
2016-11-01 20:54:27 +01:00
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
2018-05-22 20:01:18 +02:00
function createDiagnosticForNode ( node : Node , message : DiagnosticMessage , arg0? : string | number , arg1? : string | number , arg2? : string | number ) : DiagnosticWithLocation {
2016-11-01 19:05:09 +01:00
return createDiagnosticForNodeInSourceFile ( sourceFile , node , message , arg0 , arg1 , arg2 ) ;
2015-09-09 23:41:19 +02:00
}
} ) ;
}
2018-05-22 20:01:18 +02:00
function getDeclarationDiagnosticsWorker ( sourceFile : SourceFile , cancellationToken : CancellationToken ) : ReadonlyArray < DiagnosticWithLocation > {
2017-03-07 22:26:41 +01:00
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
}
2018-05-22 20:01:18 +02:00
function getAndCacheDiagnostics < T extends Diagnostic > (
2017-03-07 22:26:41 +01:00
sourceFile : SourceFile | undefined ,
cancellationToken : CancellationToken ,
2018-05-22 20:01:18 +02:00
cache : DiagnosticCache < T > ,
2018-05-22 23:46:57 +02:00
getDiagnostics : ( sourceFile : SourceFile , cancellationToken : CancellationToken ) = > T [ ] | undefined ,
2018-05-22 20:01:18 +02:00
) : ReadonlyArray < T > {
2017-03-07 22:26:41 +01:00
const cachedResult = sourceFile
? cache . perFile && cache . perFile . get ( sourceFile . path )
2018-05-22 20:01:18 +02:00
: cache . allDiagnostics as T [ ] ;
2017-03-07 22:26:41 +01:00
if ( cachedResult ) {
return cachedResult ;
}
2018-05-22 23:46:57 +02:00
const result = getDiagnostics ( sourceFile ! , cancellationToken ) || emptyArray ; // TODO: GH#18217
2017-03-07 22:26:41 +01:00
if ( sourceFile ) {
if ( ! cache . perFile ) {
2018-05-22 20:01:18 +02:00
cache . perFile = createMap < T [ ] > ( ) ;
2017-03-07 22:26:41 +01:00
}
cache . perFile . set ( sourceFile . path , result ) ;
}
else {
cache . allDiagnostics = result ;
}
return result ;
}
2018-05-22 20:01:18 +02:00
function getDeclarationDiagnosticsForFile ( sourceFile : SourceFile , cancellationToken : CancellationToken ) : ReadonlyArray < DiagnosticWithLocation > {
2017-05-17 17:12:23 +02:00
return sourceFile . isDeclarationFile ? [ ] : getDeclarationDiagnosticsWorker ( sourceFile , cancellationToken ) ;
2016-02-24 23:21:30 +01:00
}
2018-10-30 16:41:31 +01:00
function getOptionsDiagnostics ( ) : SortedReadonlyArray < 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 ( ) ,
2018-10-04 01:09:16 +02:00
getOptionsDiagnosticsOfConfigFile ( )
2017-06-06 02:01:09 +02:00
)
) ) ;
2015-05-27 05:18:13 +02:00
}
2018-10-04 01:09:16 +02:00
function getOptionsDiagnosticsOfConfigFile() {
if ( ! options . configFile ) { return emptyArray ; }
let diagnostics = programDiagnostics . getDiagnostics ( options . configFile . fileName ) ;
forEachResolvedProjectReference ( resolvedRef = > {
if ( resolvedRef ) {
diagnostics = concatenate ( diagnostics , programDiagnostics . getDiagnostics ( resolvedRef . sourceFile . fileName ) ) ;
}
} ) ;
return diagnostics ;
}
2018-10-30 16:41:31 +01:00
function getGlobalDiagnostics ( ) : SortedReadonlyArray < Diagnostic > {
2018-11-01 03:00:55 +01:00
return rootNames . length ? sortAndDeduplicateDiagnostics ( getDiagnosticsProducingTypeChecker ( ) . getGlobalDiagnostics ( ) . slice ( ) ) : emptyArray as any as SortedReadonlyArray < Diagnostic > ;
2014-12-16 22:14:14 +01:00
}
2018-03-15 22:32:01 +01:00
function getConfigFileParsingDiagnostics ( ) : ReadonlyArray < Diagnostic > {
return configFileParsingDiagnostics || emptyArray ;
}
2018-05-03 20:00:10 +02:00
function processRootFile ( fileName : string , isDefaultLib : boolean , ignoreNoDefaultLib : boolean ) {
processSourceFile ( normalizePath ( fileName ) , isDefaultLib , ignoreNoDefaultLib , /*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
2018-03-16 22:01:00 +01:00
function module NameIsEqualTo ( a : StringLiteralLike | Identifier , b : StringLiteralLike | Identifier ) : boolean {
return a . kind === SyntaxKind . Identifier
? b . kind === SyntaxKind . Identifier && a . escapedText === b . escapedText
: b . kind === SyntaxKind . StringLiteral && a . text === b . text ;
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
2018-09-12 19:44:46 +02:00
const isJavaScriptFile = isSourceFileJS ( 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
2018-03-16 22:01:00 +01:00
let imports : StringLiteralLike [ ] | undefined ;
2018-05-22 23:46:57 +02:00
let module Augmentations : ( StringLiteral | Identifier ) [ ] | undefined ;
let ambientModules : string [ ] | undefined ;
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 ) ;
2018-03-16 22:01:00 +01:00
const importDecl = createImportDeclaration ( /*decorators*/ undefined , /*modifiers*/ undefined , /*importClause*/ undefined , externalHelpersModuleReference ) ;
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 ) ;
2015-09-30 00:06:03 +02:00
}
2018-04-19 01:52:34 +02:00
if ( ( file . flags & NodeFlags . PossiblyContainsDynamicImport ) || isJavaScriptFile ) {
2018-10-24 20:27:39 +02:00
collectDynamicImportOrRequireCalls ( file ) ;
2018-04-19 01:52:34 +02: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 {
2018-03-16 22:01:00 +01:00
if ( isAnyImportOrReExport ( node ) ) {
const module NameExpr = getExternalModuleName ( node ) ;
// TypeScript 1.0 spec (April 2014): 12.1.6
// An ExternalImportDeclaration in an AmbientExternalModuleDeclaration may reference other external modules
// only through top - level external module names. Relative external module names are not permitted.
if ( module NameExpr && isStringLiteral ( module NameExpr ) && module NameExpr.text && ( ! inAmbientModule || ! isExternalModuleNameRelative ( module NameExpr.text ) ) ) {
imports = append ( imports , module NameExpr ) ;
}
}
else if ( isModuleDeclaration ( node ) ) {
if ( isAmbientModule ( node ) && ( inAmbientModule || hasModifier ( node , ModifierFlags . Ambient ) || file . isDeclarationFile ) ) {
const nameText = getTextOfIdentifierOrLiteral ( node . name ) ;
// 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 .
if ( isExternalModuleFile || ( inAmbientModule && ! isExternalModuleNameRelative ( nameText ) ) ) {
( module Augmentations || ( module Augmentations = [ ] ) ) . push ( node . name ) ;
2015-12-22 22:21:51 +01:00
}
2018-03-16 22:01:00 +01:00
else if ( ! inAmbientModule ) {
if ( file . isDeclarationFile ) {
// for global .d.ts files record name of ambient module
( ambientModules || ( ambientModules = [ ] ) ) . push ( nameText ) ;
2015-10-15 02:36:03 +02:00
}
2018-03-16 22:01:00 +01:00
// An AmbientExternalModuleDeclaration declares an external module.
// 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
// 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
}
2018-03-16 22:01:00 +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
2018-10-24 20:27:39 +02:00
function collectDynamicImportOrRequireCalls ( file : SourceFile ) {
const r = /import|require/g ;
while ( r . exec ( file . text ) !== null ) {
2018-10-26 18:50:12 +02:00
const node = getNodeAtPosition ( file , r . lastIndex ) ;
2018-10-24 20:27:39 +02:00
if ( isRequireCall ( node , /*checkArgumentIsStringLiteralLike*/ true ) ) {
imports = append ( imports , node . arguments [ 0 ] ) ;
}
// 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 && isStringLiteralLike ( node . arguments [ 0 ] ) ) {
imports = append ( imports , node . arguments [ 0 ] as StringLiteralLike ) ;
}
else if ( isLiteralImportTypeNode ( node ) ) {
imports = append ( imports , node . argument . literal ) ;
}
2015-06-23 02:48:44 +02:00
}
}
2018-04-02 19:18:23 +02:00
2018-10-26 18:50:12 +02:00
/** Returns a token if position is in [start-of-leading-trivia, end), includes JSDoc only in JS files */
function getNodeAtPosition ( sourceFile : SourceFile , position : number ) : Node {
2018-10-24 20:27:39 +02:00
let current : Node = sourceFile ;
const getContainingChild = ( child : Node ) = > {
if ( child . pos <= position && ( position < child . end || ( position === child . end && ( child . kind === SyntaxKind . EndOfFileToken ) ) ) ) {
return child ;
}
} ;
while ( true ) {
2018-10-26 18:50:12 +02:00
const child = isJavaScriptFile && hasJSDocNodes ( current ) && forEach ( current . jsDoc , getContainingChild ) || forEachChild ( current , getContainingChild ) ;
2018-10-24 20:27:39 +02:00
if ( ! child ) {
return current ;
}
current = child ;
}
2018-04-02 19:18:23 +02:00
}
2014-12-16 22:14:14 +01:00
}
2018-05-31 18:50:51 +02:00
function getLibFileFromReference ( ref : FileReference ) {
const libName = ref . fileName . toLocaleLowerCase ( ) ;
const libFileName = libMap . get ( libName ) ;
if ( libFileName ) {
return getSourceFile ( combinePaths ( defaultLibraryPath , libFileName ) ) ;
}
}
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 {
2018-12-06 00:31:55 +01:00
return getSourceFileFromReferenceWorker ( resolveTripleslashReference ( ref . fileName , referencingFile . fileName ) , fileName = > filesByName . get ( toPath ( fileName ) ) || undefined ) ;
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 ) ) {
2018-10-11 23:19:32 +02:00
if ( ! options . allowNonTsExtensions && ! forEach ( supportedExtensionsWithJsonIfResolveJsonModule , 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 ) {
2018-05-08 00:12:50 +02:00
const redirect = getProjectReferenceRedirect ( fileName ) ;
if ( redirect ) {
fail ( Diagnostics . Output_file_0_has_not_been_built_from_source_file_1 , redirect , fileName ) ;
}
else {
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`. */
2018-05-03 20:00:10 +02:00
function processSourceFile ( fileName : string , isDefaultLib : boolean , ignoreNoDefaultLib : boolean , packageId : PackageId | undefined , refFile? : SourceFile , refPos? : number , refEnd? : number ) : void {
2017-05-09 21:20:52 +02:00
getSourceFileFromReferenceWorker ( fileName ,
2018-05-29 22:46:32 +02:00
fileName = > findSourceFile ( fileName , toPath ( fileName ) , isDefaultLib , ignoreNoDefaultLib , refFile ! , refPos ! , refEnd ! , packageId ) , // TODO: GH#18217
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
2018-10-01 20:49:43 +02:00
function createRedirectSourceFile ( redirectTarget : SourceFile , unredirected : SourceFile , fileName : string , path : Path , resolvedPath : Path , originalFileName : string ) : SourceFile {
2019-01-17 20:54:43 +01:00
const redirect : SourceFile = Object . create ( redirectTarget ) ;
2017-08-09 23:39:06 +02:00
redirect . fileName = fileName ;
redirect . path = path ;
2018-10-01 20:49:43 +02:00
redirect . resolvedPath = resolvedPath ;
redirect . originalFileName = originalFileName ;
2017-08-09 23:39:06 +02:00
redirect . redirectInfo = { redirectTarget , unredirected } ;
2018-10-16 18:01:25 +02:00
sourceFilesFoundSearchingNodeModules . set ( path , currentNodeModulesDepth > 0 ) ;
2017-08-09 23:39:06 +02:00
Object . defineProperties ( redirect , {
id : {
2018-05-22 23:46:57 +02:00
get ( this : SourceFile ) { return this . redirectInfo ! . redirectTarget . id ; } ,
set ( this : SourceFile , value : SourceFile [ "id" ] ) { this . redirectInfo ! . redirectTarget . id = value ; } ,
2017-08-09 23:39:06 +02:00
} ,
symbol : {
2018-05-22 23:46:57 +02:00
get ( this : SourceFile ) { return this . redirectInfo ! . redirectTarget . symbol ; } ,
set ( this : SourceFile , value : SourceFile [ "symbol" ] ) { this . redirectInfo ! . redirectTarget . symbol = value ; } ,
2017-08-09 23:39:06 +02:00
} ,
} ) ;
return redirect ;
}
2015-10-15 23:43:51 +02:00
// Get source file from normalized fileName
2018-05-03 20:00:10 +02:00
function findSourceFile ( fileName : string , path : Path , isDefaultLib : boolean , ignoreNoDefaultLib : boolean , refFile : SourceFile , refPos : number , refEnd : number , packageId : PackageId | undefined ) : SourceFile | undefined {
2018-09-18 03:24:12 +02:00
const originalFileName = fileName ;
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
2018-07-25 00:24:52 +02:00
// NOTE: this only makes sense for case-insensitive file systems, and only on files which are not redirected
if ( file && options . forceConsistentCasingInFileNames ) {
let inputName = fileName ;
const checkedName = file . fileName ;
const isRedirect = toPath ( checkedName ) !== toPath ( inputName ) ;
if ( isRedirect ) {
inputName = getProjectReferenceRedirect ( fileName ) || fileName ;
}
if ( getNormalizedAbsolutePath ( checkedName , currentDirectory ) !== getNormalizedAbsolutePath ( inputName , currentDirectory ) ) {
reportFileNamesDifferOnlyInCasingError ( inputName , checkedName , refFile , refPos , refEnd ) ;
}
2015-10-15 23:43:51 +02:00
}
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 ) ;
}
2018-12-14 14:26:32 +01:00
if ( ! options . noLib ) {
processLibReferenceDirectives ( file ) ;
}
2018-05-05 02:23:56 +02:00
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
}
}
2018-12-06 00:31:55 +01:00
return file || undefined ;
2014-12-16 22:14:14 +01:00
}
2018-10-02 22:11:12 +02:00
let redirectedPath : Path | undefined ;
2018-05-08 00:12:50 +02:00
if ( refFile ) {
const redirect = getProjectReferenceRedirect ( fileName ) ;
if ( redirect ) {
( ( refFile . redirectedReferences || ( refFile . redirectedReferences = [ ] ) ) as string [ ] ) . push ( fileName ) ;
fileName = redirect ;
// Once we start redirecting to a file, we can potentially come back to it
// via a back-reference from another file in the .d.ts folder. If that happens we'll
// end up trying to add it to the program *again* because we were tracking it via its
// original (un-redirected) name. So we have to map both the original path and the redirected path
// to the source file we're about to find/create
redirectedPath = toPath ( redirect ) ;
}
}
2015-09-27 06:29:07 +02:00
// We haven't looked for this file, do so now and cache result
2018-05-22 23:46:57 +02:00
const file = host . getSourceFile ( fileName , options . target ! , hostErrorMessage = > { // TODO: GH#18217
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.
2018-10-01 20:49:43 +02:00
const dupFile = createRedirectSourceFile ( fileFromPackageId , file ! , fileName , path , toPath ( fileName ) , originalFileName ) ; // TODO: GH#18217
2018-08-01 02:28:56 +02:00
redirectTargetsMap . add ( fileFromPackageId . path , fileName ) ;
2018-10-02 22:11:12 +02:00
addFileToFilesByName ( dupFile , path , redirectedPath ) ;
2017-08-09 23:39:06 +02:00
sourceFileToPackageName . set ( path , packageId . name ) ;
2018-05-29 22:46:32 +02:00
processingOtherFiles ! . push ( dupFile ) ;
2017-08-09 23:39:06 +02:00
return dupFile ;
}
else if ( file ) {
// This is the first source file to have this packageId.
packageIdToSourceFile . set ( packageIdKey , file ) ;
sourceFileToPackageName . set ( path , packageId . name ) ;
}
}
2018-10-02 22:11:12 +02:00
addFileToFilesByName ( file , path , redirectedPath ) ;
2018-05-08 00:12:50 +02:00
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 ;
2018-06-01 22:12:57 +02:00
file . resolvedPath = toPath ( fileName ) ;
2018-09-18 03:24:12 +02:00
file . originalFileName = originalFileName ;
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
2018-05-22 23:46:57 +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 {
2018-05-22 23:46:57 +02:00
filesByNameIgnoreCase ! . set ( pathLowerCase , file ) ;
2015-10-15 23:43:51 +02:00
}
}
2014-12-16 22:14:14 +01:00
2018-05-03 20:00:10 +02:00
skipDefaultLib = skipDefaultLib || ( file . hasNoDefaultLib && ! ignoreNoDefaultLib ) ;
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
}
2018-12-14 14:26:32 +01:00
if ( ! options . noLib ) {
processLibReferenceDirectives ( file ) ;
}
2015-08-19 20:58:02 +02:00
2018-05-05 02:23:56 +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 ) {
2018-05-29 22:46:32 +02:00
processingDefaultLibFiles ! . push ( file ) ;
2015-09-27 06:29:07 +02:00
}
else {
2018-05-29 22:46:32 +02:00
processingOtherFiles ! . 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
}
2018-10-02 22:11:12 +02:00
function addFileToFilesByName ( file : SourceFile | undefined , path : Path , redirectedPath : Path | undefined ) {
if ( redirectedPath ) {
filesByName . set ( redirectedPath , file ) ;
2018-12-06 00:31:55 +01:00
filesByName . set ( path , file || false ) ;
}
else {
filesByName . set ( path , file ) ;
2018-10-02 22:11:12 +02:00
}
}
2018-05-08 00:12:50 +02:00
function getProjectReferenceRedirect ( fileName : string ) : string | undefined {
2018-09-18 20:44:16 +02:00
// Ignore dts or any of the non ts files
2018-09-19 01:21:05 +02:00
if ( ! resolvedProjectReferences || ! resolvedProjectReferences . length || fileExtensionIs ( fileName , Extension . Dts ) || ! fileExtensionIsOneOf ( fileName , supportedTSExtensions ) ) {
2018-09-18 20:44:16 +02:00
return undefined ;
}
2018-05-08 00:12:50 +02:00
// If this file is produced by a referenced project, we need to rewrite it to
// look in the output folder of the referenced project rather than the input
2018-10-02 23:10:19 +02:00
const referencedProject = getResolvedProjectReferenceToRedirect ( fileName ) ;
2018-10-02 00:30:06 +02:00
if ( ! referencedProject ) {
return undefined ;
}
const out = referencedProject . commandLine . options . outFile || referencedProject . commandLine . options . out ;
return out ?
changeExtension ( out , Extension . Dts ) :
2019-03-13 17:58:26 +01:00
getOutputDeclarationFileName ( fileName , referencedProject . commandLine , ! host . useCaseSensitiveFileNames ( ) ) ;
2018-10-02 00:30:06 +02:00
}
/ * *
* Get the referenced project if the file is input file from that reference project
* /
2018-10-02 23:10:19 +02:00
function getResolvedProjectReferenceToRedirect ( fileName : string ) {
2018-11-19 21:58:49 +01:00
if ( mapFromFileToProjectReferenceRedirects === undefined ) {
mapFromFileToProjectReferenceRedirects = createMap ( ) ;
forEachResolvedProjectReference ( ( referencedProject , referenceProjectPath ) = > {
// not input file from the referenced project, ignore
if ( referencedProject &&
toPath ( options . configFilePath ! ) !== referenceProjectPath ) {
referencedProject . commandLine . fileNames . forEach ( f = >
mapFromFileToProjectReferenceRedirects ! . set ( toPath ( f ) , referenceProjectPath ) ) ;
}
} ) ;
}
2018-09-18 20:44:16 +02:00
2018-11-19 21:58:49 +01:00
const referencedProjectPath = mapFromFileToProjectReferenceRedirects . get ( toPath ( fileName ) ) ;
return referencedProjectPath && getResolvedProjectReferenceByPath ( referencedProjectPath ) ;
2018-05-08 00:12:50 +02:00
}
2018-10-02 23:10:19 +02:00
function forEachResolvedProjectReference < T > (
cb : ( resolvedProjectReference : ResolvedProjectReference | undefined , resolvedProjectReferencePath : Path ) = > T | undefined
) : T | undefined {
return forEachProjectReference ( projectReferences , resolvedProjectReferences , ( resolvedRef , index , parent ) = > {
const ref = ( parent ? parent.commandLine.projectReferences : projectReferences ) ! [ index ] ;
const resolvedRefPath = toPath ( resolveProjectReferencePath ( ref ) ) ;
return cb ( resolvedRef , resolvedRefPath ) ;
} ) ;
}
function forEachProjectReference < T > (
projectReferences : ReadonlyArray < ProjectReference > | undefined ,
resolvedProjectReferences : ReadonlyArray < ResolvedProjectReference | undefined > | undefined ,
cbResolvedRef : ( resolvedRef : ResolvedProjectReference | undefined , index : number , parent : ResolvedProjectReference | undefined ) = > T | undefined ,
cbRef ? : ( projectReferences : ReadonlyArray < ProjectReference > | undefined , parent : ResolvedProjectReference | undefined ) = > T | undefined
) : T | undefined {
let seenResolvedRefs : ResolvedProjectReference [ ] | undefined ;
return worker ( projectReferences , resolvedProjectReferences , /*parent*/ undefined , cbResolvedRef , cbRef ) ;
function worker (
projectReferences : ReadonlyArray < ProjectReference > | undefined ,
resolvedProjectReferences : ReadonlyArray < ResolvedProjectReference | undefined > | undefined ,
parent : ResolvedProjectReference | undefined ,
cbResolvedRef : ( resolvedRef : ResolvedProjectReference | undefined , index : number , parent : ResolvedProjectReference | undefined ) = > T | undefined ,
cbRef ? : ( projectReferences : ReadonlyArray < ProjectReference > | undefined , parent : ResolvedProjectReference | undefined ) = > T | undefined ,
) : T | undefined {
// Visit project references first
if ( cbRef ) {
const result = cbRef ( projectReferences , parent ) ;
if ( result ) { return result ; }
}
return forEach ( resolvedProjectReferences , ( resolvedRef , index ) = > {
if ( contains ( seenResolvedRefs , resolvedRef ) ) {
// ignore recursives
return undefined ;
}
const result = cbResolvedRef ( resolvedRef , index , parent ) ;
if ( result ) {
return result ;
}
if ( ! resolvedRef ) return undefined ;
( seenResolvedRefs || ( seenResolvedRefs = [ ] ) ) . push ( resolvedRef ) ;
return worker ( resolvedRef . commandLine . projectReferences , resolvedRef . references , resolvedRef , cbResolvedRef , cbRef ) ;
} ) ;
}
}
function getResolvedProjectReferenceByPath ( projectReferencePath : Path ) : ResolvedProjectReference | undefined {
if ( ! projectReferenceRedirects ) {
return undefined ;
}
return projectReferenceRedirects . get ( projectReferencePath ) || undefined ;
}
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 = > {
2018-09-18 03:24:12 +02:00
const referencedFileName = resolveTripleslashReference ( ref . fileName , file . originalFileName ) ;
2018-05-03 20:00:10 +02:00
processSourceFile ( referencedFileName , isDefaultLib , /*ignoreNoDefaultLib*/ false , /*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 ( ) ) ;
2018-05-08 00:12:50 +02:00
if ( ! typeDirectives ) {
return ;
}
2018-10-02 23:10:19 +02:00
const resolutions = resolveTypeReferenceDirectiveNamesWorker ( typeDirectives , file . originalFileName , getResolvedProjectReferenceToRedirect ( file . originalFileName ) ) ;
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
}
}
2018-10-22 21:12:12 +02:00
function processTypeReferenceDirective ( typeReferenceDirective : string , resolvedTypeReferenceDirective? : ResolvedTypeReferenceDirective ,
2016-04-06 22:49:25 +02:00
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 ) {
2018-10-20 03:00:45 +02:00
if ( resolvedTypeReferenceDirective . isExternalLibraryImport ) currentNodeModulesDepth ++ ;
2016-04-06 22:49:25 +02:00
if ( resolvedTypeReferenceDirective . primary ) {
// resolved from the primary path
2018-05-29 22:46:32 +02:00
processSourceFile ( resolvedTypeReferenceDirective . resolvedFileName ! , /*isDefaultLib*/ false , /*ignoreNoDefaultLib*/ false , resolvedTypeReferenceDirective . packageId , refFile , refPos , refEnd ) ; // TODO: GH#18217
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 ) {
2018-05-22 23:46:57 +02:00
const otherFileText = host . readFile ( resolvedTypeReferenceDirective . resolvedFileName ! ) ;
if ( otherFileText !== getSourceFile ( previousResolution . resolvedFileName ! ) ! . text ) {
fileProcessingDiagnostics . add ( createDiagnostic ( refFile ! , refPos ! , refEnd ! , // TODO: GH#18217
2016-11-09 16:41:25 +01:00
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
2018-05-29 22:46:32 +02:00
processSourceFile ( resolvedTypeReferenceDirective . resolvedFileName ! , /*isDefaultLib*/ false , /*ignoreNoDefaultLib*/ false , resolvedTypeReferenceDirective . packageId , refFile , refPos , refEnd ) ;
2016-02-23 21:48:31 +01:00
}
}
2018-10-20 03:00:45 +02:00
if ( resolvedTypeReferenceDirective . isExternalLibraryImport ) currentNodeModulesDepth -- ;
2016-04-06 22:49:25 +02:00
}
else {
2018-05-22 23:46:57 +02:00
fileProcessingDiagnostics . add ( createDiagnostic ( refFile ! , refPos ! , refEnd ! , Diagnostics . Cannot_find_type_definition_file_for_0 , typeReferenceDirective ) ) ; // TODO: GH#18217
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
}
}
2018-05-03 20:00:10 +02:00
function processLibReferenceDirectives ( file : SourceFile ) {
forEach ( file . libReferenceDirectives , libReference = > {
const libName = libReference . fileName . toLocaleLowerCase ( ) ;
const libFileName = libMap . get ( libName ) ;
if ( libFileName ) {
// we ignore any 'no-default-lib' reference set on this file.
processRootFile ( combinePaths ( defaultLibraryPath , libFileName ) , /*isDefaultLib*/ true , /*ignoreNoDefaultLib*/ true ) ;
}
else {
const unqualifiedLibName = removeSuffix ( removePrefix ( libName , "lib." ) , ".d.ts" ) ;
const suggestion = getSpellingSuggestion ( unqualifiedLibName , libs , identity ) ;
const message = suggestion ? Diagnostics.Cannot_find_lib_definition_for_0_Did_you_mean_1 : Diagnostics.Cannot_find_lib_definition_for_0 ;
fileProcessingDiagnostics . add ( createDiagnostic ( file , libReference . pos , libReference . end , message , libName , suggestion ) ) ;
}
} ) ;
}
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 ) ;
2018-10-21 18:15:01 +02:00
const resolutions = resolveModuleNamesReusingOldState ( module Names , getNormalizedAbsolutePath ( file . originalFileName , currentDirectory ) , file ) ;
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 ;
2018-09-14 00:05:57 +02:00
const isJsFile = ! resolutionExtensionIsTSOrJson ( resolution . extension ) ;
2017-09-19 21:42:29 +02:00
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
2018-04-19 01:52:34 +02:00
&& ! ( isJsFile && ! options . allowJs )
2018-09-12 19:44:46 +02:00
&& ( isInJSFile ( file . imports [ i ] ) || ! ( file . imports [ i ] . flags & NodeFlags . JSDoc ) ) ;
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 ) ;
2018-05-03 20:00:10 +02:00
findSourceFile ( resolvedFileName , path , /*isDefaultLib*/ false , /*ignoreNoDefaultLib*/ 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 {
2018-08-28 02:03:30 +02:00
const fileNames = mapDefined ( sourceFiles , file = > file . isDeclarationFile ? undefined : file . fileName ) ;
2016-04-01 21:41:01 +02:00
return computeCommonSourceDirectoryOfFilenames ( fileNames , currentDirectory , getCanonicalFileName ) ;
2016-02-23 21:48:31 +01:00
}
2018-08-28 02:03:30 +02:00
function checkSourceFilesBelongToPath ( sourceFiles : ReadonlyArray < SourceFile > , rootDirectory : string ) : boolean {
2015-04-15 07:11:25 +02:00
let allFilesBelongToPath = true ;
2018-08-28 02:03:30 +02:00
const absoluteRootDirectoryPath = host . getCanonicalFileName ( getNormalizedAbsolutePath ( rootDirectory , currentDirectory ) ) ;
for ( const sourceFile of sourceFiles ) {
if ( ! sourceFile . isDeclarationFile ) {
const absoluteSourceFilePath = host . getCanonicalFileName ( getNormalizedAbsolutePath ( sourceFile . fileName , currentDirectory ) ) ;
if ( absoluteSourceFilePath . indexOf ( absoluteRootDirectoryPath ) !== 0 ) {
programDiagnostics . add ( createCompilerDiagnostic ( Diagnostics . File_0_is_not_under_rootDir_1_rootDir_is_expected_to_contain_all_source_files , sourceFile . fileName , rootDirectory ) ) ;
allFilesBelongToPath = false ;
2015-04-15 07:11:25 +02:00
}
}
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
}
2018-09-19 01:21:05 +02:00
function parseProjectReferenceConfigFile ( ref : ProjectReference ) : ResolvedProjectReference | undefined {
if ( ! projectReferenceRedirects ) {
projectReferenceRedirects = createMap < ResolvedProjectReference | false > ( ) ;
}
2018-05-08 00:12:50 +02:00
// The actual filename (i.e. add "/tsconfig.json" if necessary)
2018-09-11 21:56:23 +02:00
const refPath = resolveProjectReferencePath ( ref ) ;
2018-09-19 01:21:05 +02:00
const sourceFilePath = toPath ( refPath ) ;
const fromCache = projectReferenceRedirects . get ( sourceFilePath ) ;
if ( fromCache !== undefined ) {
return fromCache || undefined ;
}
2018-05-08 00:12:50 +02:00
// An absolute path pointing to the containing directory of the config file
const basePath = getNormalizedAbsolutePath ( getDirectoryPath ( refPath ) , host . getCurrentDirectory ( ) ) ;
2018-07-06 00:39:03 +02:00
const sourceFile = host . getSourceFile ( refPath , ScriptTarget . JSON ) as JsonSourceFile | undefined ;
2018-10-02 23:10:19 +02:00
addFileToFilesByName ( sourceFile , sourceFilePath , /*redirectedPath*/ undefined ) ;
2018-05-08 00:12:50 +02:00
if ( sourceFile === undefined ) {
2018-09-19 01:21:05 +02:00
projectReferenceRedirects . set ( sourceFilePath , false ) ;
2018-05-08 00:12:50 +02:00
return undefined ;
}
2018-09-19 01:21:05 +02:00
sourceFile . path = sourceFilePath ;
2018-10-02 23:10:19 +02:00
sourceFile . resolvedPath = sourceFilePath ;
sourceFile . originalFileName = refPath ;
2018-05-08 00:12:50 +02:00
const commandLine = parseJsonSourceFileConfigFileContent ( sourceFile , configParsingHost , basePath , /*existingOptions*/ undefined , refPath ) ;
2018-09-19 01:21:05 +02:00
const resolvedRef : ResolvedProjectReference = { commandLine , sourceFile } ;
projectReferenceRedirects . set ( sourceFilePath , resolvedRef ) ;
if ( commandLine . projectReferences ) {
resolvedRef . references = commandLine . projectReferences . map ( parseProjectReferenceConfigFile ) ;
}
return resolvedRef ;
2018-05-08 00:12:50 +02:00
}
2014-12-16 22:14:14 +01:00
function verifyCompilerOptions() {
2018-07-20 00:16:59 +02:00
if ( options . strictPropertyInitialization && ! getStrictOptionValue ( options , "strictNullChecks" ) ) {
2018-04-24 20:48:46 +02:00
createDiagnosticForOptionName ( Diagnostics . Option_0_cannot_be_specified_without_specifying_option_1 , "strictPropertyInitialization" , "strictNullChecks" ) ;
}
2015-05-19 06:49:41 +02:00
if ( options . isolatedModules ) {
2018-09-18 22:16:25 +02:00
if ( getEmitDeclarations ( options ) ) {
createDiagnosticForOptionName ( Diagnostics . Option_0_cannot_be_specified_with_option_1 , getEmitDeclarationOptionName ( options ) , "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
}
2018-05-08 00:12:50 +02:00
if ( options . composite ) {
if ( options . declaration === false ) {
createDiagnosticForOptionName ( Diagnostics . Composite_projects_may_not_disable_declaration_emit , "declaration" ) ;
2019-03-01 00:39:04 +01:00
}
if ( options . incremental === false ) {
2019-03-01 19:33:24 +01:00
createDiagnosticForOptionName ( Diagnostics . Composite_projects_may_not_disable_incremental_compilation , "declaration" ) ;
2018-05-08 00:12:50 +02:00
}
}
2019-03-01 18:58:16 +01:00
if ( options . tsBuildInfoFile ) {
if ( ! isIncrementalCompilation ( options ) ) {
createDiagnosticForOptionName ( Diagnostics . Option_0_cannot_be_specified_without_specifying_option_1_or_option_2 , "tsBuildInfoFile" , "incremental" , "composite" ) ;
}
}
2018-10-04 01:09:16 +02:00
verifyProjectReferences ( ) ;
2018-05-08 00:12:50 +02:00
// List of collected files is complete; validate exhautiveness if this is a project with a file list
2018-09-04 13:35:57 +02:00
if ( options . composite ) {
2019-03-14 22:15:06 +01:00
const rootPaths = rootNames . map ( toPath ) ;
2019-03-14 18:58:57 +01:00
for ( const file of files ) {
// Ignore declaration files
if ( file . isDeclarationFile ) continue ;
// Ignore json file thats from project reference
if ( isJsonSourceFile ( file ) && getResolvedProjectReferenceToRedirect ( file . fileName ) ) continue ;
if ( rootPaths . indexOf ( file . path ) === - 1 ) {
programDiagnostics . add ( createCompilerDiagnostic ( Diagnostics . File_0_is_not_in_project_file_list_Projects_must_list_all_files_or_use_an_include_pattern , file . fileName ) ) ;
2018-05-08 00:12:50 +02:00
}
}
}
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
}
2018-03-26 21:15:34 +02:00
if ( options . mapRoot && ! ( options . sourceMap || options . declarationMap ) ) {
2016-06-09 20:51:53 +02:00
// Error to specify --mapRoot without --sourcemap
2018-03-26 21:15:34 +02:00
createDiagnosticForOptionName ( Diagnostics . Option_0_cannot_be_specified_without_specifying_option_1_or_option_2 , "mapRoot" , "sourceMap" , "declarationMap" ) ;
2014-12-16 22:14:14 +01:00
}
2016-02-23 21:12:52 +01:00
if ( options . declarationDir ) {
2018-09-18 10:33:56 +02:00
if ( ! getEmitDeclarations ( options ) ) {
2018-09-18 22:16:25 +02:00
createDiagnosticForOptionName ( Diagnostics . Option_0_cannot_be_specified_without_specifying_option_1_or_option_2 , "declarationDir" , "declaration" , "composite" ) ;
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
}
2018-07-09 21:32:52 +02:00
if ( options . declarationMap && ! getEmitDeclarations ( options ) ) {
2018-09-18 22:16:25 +02:00
createDiagnosticForOptionName ( Diagnostics . Option_0_cannot_be_specified_without_specifying_option_1_or_option_2 , "declarationMap" , "declaration" , "composite" ) ;
2018-03-26 21:15:34 +02: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
2018-10-29 19:56:49 +01:00
const firstNonAmbientExternalModuleSourceFile = find ( files , f = > isExternalModule ( f ) && ! f . isDeclarationFile ) ;
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
}
2018-10-29 19:56:49 +01:00
const firstNonExternalModuleSourceFile = find ( files , f = > ! isExternalModule ( f ) && ! f . isDeclarationFile && f . scriptKind !== ScriptKind . JSON ) ;
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
2018-05-22 23:46:57 +02:00
const span = getErrorSpanForNode ( firstNonAmbientExternalModuleSourceFile , firstNonAmbientExternalModuleSourceFile . externalModuleIndicator ! ) ;
2016-07-23 00:41:52 +02:00
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
2018-09-29 03:07:48 +02:00
if ( outFile && ! options . emitDeclarationOnly ) {
2016-05-18 02:14:51 +02:00
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 ) {
2018-05-22 23:46:57 +02:00
const span = getErrorSpanForNode ( firstNonAmbientExternalModuleSourceFile , firstNonAmbientExternalModuleSourceFile . externalModuleIndicator ! ) ;
2016-07-23 00:41:52 +02:00
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
}
2018-04-06 00:27:44 +02:00
if ( options . resolveJsonModule ) {
if ( getEmitModuleResolutionKind ( options ) !== ModuleResolutionKind . NodeJs ) {
createDiagnosticForOptionName ( Diagnostics . Option_resolveJsonModule_cannot_be_specified_without_node_module_resolution_strategy , "resolveJsonModule" ) ;
}
2018-08-31 22:05:52 +02:00
// Any emit other than common js, amd, es2015 or esnext is error
else if ( ! hasJsonModuleEmitEnabled ( options ) ) {
createDiagnosticForOptionName ( Diagnostics . Option_resolveJsonModule_can_only_be_specified_when_module_code_generation_is_commonjs_amd_es2015_or_esNext , "resolveJsonModule" , "module" ) ;
2018-07-11 00:00:18 +02:00
}
2018-04-06 00:27:44 +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
2018-10-29 19:56:49 +01:00
if ( options . outDir && dir === "" && files . some ( 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
}
2018-09-18 13:46:42 +02:00
if ( ! options . noEmit && options . allowJs && getEmitDeclarations ( options ) ) {
2018-09-18 22:16:25 +02:00
createDiagnosticForOptionName ( Diagnostics . Option_0_cannot_be_specified_with_option_1 , "allowJs" , getEmitDeclarationOptionName ( options ) ) ;
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-09-18 22:16:25 +02:00
if ( ! getEmitDeclarations ( options ) ) {
createDiagnosticForOptionName ( Diagnostics . Option_0_cannot_be_specified_without_specifying_option_1_or_option_2 , "emitDeclarationOnly" , "declaration" , "composite" ) ;
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
2018-05-22 23:46:57 +02:00
function verifyEmitFilePath ( emitFileName : string | undefined , 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 ) ) {
2018-05-22 23:46:57 +02:00
let chain : DiagnosticMessageChain | undefined ;
2016-11-10 23:12:24 +01:00
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
}
}
2018-10-04 01:09:16 +02:00
function verifyProjectReferences() {
2019-02-28 01:15:09 +01:00
const buildInfoPath = ! options . noEmit && ! options . suppressOutputPathCheck ? getOutputPathForBuildInfo ( options ) : undefined ;
2018-10-04 01:09:16 +02:00
forEachProjectReference ( projectReferences , resolvedProjectReferences , ( resolvedRef , index , parent ) = > {
const ref = ( parent ? parent.commandLine.projectReferences : projectReferences ) ! [ index ] ;
const parentFile = parent && parent . sourceFile as JsonSourceFile ;
if ( ! resolvedRef ) {
createDiagnosticForReference ( parentFile , index , Diagnostics . File_0_not_found , ref . path ) ;
return ;
}
const options = resolvedRef . commandLine . options ;
if ( ! options . composite ) {
2018-10-30 23:22:00 +01:00
// ok to not have composite if the current program is container only
const inputs = parent ? parent.commandLine.fileNames : rootNames ;
if ( inputs . length ) {
createDiagnosticForReference ( parentFile , index , Diagnostics . Referenced_project_0_must_have_setting_composite_Colon_true , ref . path ) ;
}
2018-10-04 01:09:16 +02:00
}
if ( ref . prepend ) {
const out = options . outFile || options . out ;
if ( out ) {
if ( ! host . fileExists ( out ) ) {
createDiagnosticForReference ( parentFile , index , Diagnostics . Output_file_0_from_project_1_does_not_exist , out , ref . path ) ;
}
}
else {
createDiagnosticForReference ( parentFile , index , Diagnostics . Cannot_prepend_project_0_because_it_does_not_have_outFile_set , ref . path ) ;
}
}
2019-02-28 01:15:09 +01:00
if ( ! parent && buildInfoPath && buildInfoPath === getOutputPathForBuildInfo ( options ) ) {
createDiagnosticForReference ( parentFile , index , Diagnostics . Cannot_write_file_0_because_it_will_overwrite_tsbuildinfo_file_generated_by_referenced_project_1 , buildInfoPath , ref . path ) ;
2019-02-22 04:05:30 +01:00
hasEmitBlockingDiagnostics . set ( toPath ( buildInfoPath ) , true ) ;
}
2018-10-04 01:09:16 +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 ) ) {
2018-05-22 23:46:57 +02:00
const initializer = keyProps . initializer ;
if ( isArrayLiteralExpression ( initializer ) && initializer . elements . length > valueIndex ) {
programDiagnostics . add ( createDiagnosticForNodeInSourceFile ( options . configFile ! , initializer . elements [ valueIndex ] , message , arg0 , arg1 , arg2 ) ) ;
2016-11-29 21:42:17 +01:00
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 ) ) ;
}
}
2018-05-08 00:12:50 +02:00
function getOptionsSyntaxByName ( name : string ) : object | undefined {
2016-11-29 21:42:17 +01:00
const compilerOptionsObjectLiteralSyntax = getCompilerOptionsObjectLiteralSyntax ( ) ;
if ( compilerOptionsObjectLiteralSyntax ) {
2018-05-08 00:12:50 +02:00
return getPropertyAssignment ( compilerOptionsObjectLiteralSyntax , name ) ;
2016-11-29 21:42:17 +01:00
}
2018-05-08 00:12:50 +02:00
return undefined ;
}
function getOptionPathsSyntax ( ) : PropertyAssignment [ ] {
return getOptionsSyntaxByName ( "paths" ) as PropertyAssignment [ ] || emptyArray ;
2016-11-29 21:42:17 +01:00
}
2018-03-26 21:15:34 +02:00
function createDiagnosticForOptionName ( message : DiagnosticMessage , option1 : string , option2? : string , option3? : string ) {
createDiagnosticForOption ( /*onKey*/ true , option1 , option2 , message , option1 , option2 , option3 ) ;
2016-11-29 21:42:17 +01:00
}
function createOptionValueDiagnostic ( option1 : string , message : DiagnosticMessage , arg0 : string ) {
createDiagnosticForOption ( /*onKey*/ false , option1 , /*option2*/ undefined , message , arg0 ) ;
}
2018-10-04 01:09:16 +02:00
function createDiagnosticForReference ( sourceFile : JsonSourceFile | undefined , index : number , message : DiagnosticMessage , arg0? : string | number , arg1? : string | number ) {
const referencesSyntax = firstDefined ( getTsConfigPropArray ( sourceFile || options . configFile , "references" ) ,
property = > isArrayLiteralExpression ( property . initializer ) ? property.initializer : undefined ) ;
2018-07-21 02:22:25 +02:00
if ( referencesSyntax && referencesSyntax . elements . length > index ) {
2018-10-04 01:09:16 +02:00
programDiagnostics . add ( createDiagnosticForNodeInSourceFile ( sourceFile || options . configFile ! , referencesSyntax . elements [ index ] , message , arg0 , arg1 ) ) ;
2018-07-21 02:22:25 +02:00
}
else {
programDiagnostics . add ( createCompilerDiagnostic ( message , arg0 , arg1 ) ) ;
2018-05-08 00:12:50 +02:00
}
}
2018-05-22 23:46:57 +02:00
function createDiagnosticForOption ( onKey : boolean , option1 : string , option2 : string | undefined , message : DiagnosticMessage , arg0 : string | number , arg1? : string | number , arg2? : string | number ) {
2016-11-29 21:42:17 +01:00
const compilerOptionsObjectLiteralSyntax = getCompilerOptionsObjectLiteralSyntax ( ) ;
const needCompilerDiagnostic = ! compilerOptionsObjectLiteralSyntax ||
2018-03-26 21:15:34 +02:00
! createOptionDiagnosticInObjectLiteralSyntax ( compilerOptionsObjectLiteralSyntax , onKey , option1 , option2 , message , arg0 , arg1 , arg2 ) ;
2016-11-29 21:42:17 +01:00
if ( needCompilerDiagnostic ) {
2018-03-26 21:15:34 +02:00
programDiagnostics . add ( createCompilerDiagnostic ( message , arg0 , arg1 , arg2 ) ) ;
2016-11-29 21:42:17 +01:00
}
}
function getCompilerOptionsObjectLiteralSyntax() {
if ( _compilerOptionsObjectLiteralSyntax === undefined ) {
_compilerOptionsObjectLiteralSyntax = null ; // tslint:disable-line:no-null-keyword
2018-01-30 19:53:54 +01:00
const jsonObjectLiteral = getTsConfigObjectLiteralExpression ( options . configFile ) ;
if ( jsonObjectLiteral ) {
for ( const prop of getPropertyAssignment ( jsonObjectLiteral , "compilerOptions" ) ) {
2016-11-29 21:42:17 +01:00
if ( isObjectLiteralExpression ( prop . initializer ) ) {
_compilerOptionsObjectLiteralSyntax = prop . initializer ;
break ;
}
}
}
}
return _compilerOptionsObjectLiteralSyntax ;
}
2018-05-22 23:46:57 +02:00
function createOptionDiagnosticInObjectLiteralSyntax ( objectLiteral : ObjectLiteralExpression , onKey : boolean , key1 : string , key2 : string | undefined , message : DiagnosticMessage , arg0 : string | number , arg1? : string | number , arg2? : string | number ) : boolean {
2016-11-29 21:42:17 +01:00
const props = getPropertyAssignment ( objectLiteral , key1 , key2 ) ;
for ( const prop of props ) {
2018-05-22 23:46:57 +02:00
programDiagnostics . add ( createDiagnosticForNodeInSourceFile ( options . configFile ! , onKey ? prop.name : prop.initializer , message , arg0 , arg1 , arg2 ) ) ;
2016-11-29 21:42:17 +01:00
}
return ! ! props . length ;
}
2018-05-08 00:12:50 +02:00
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
2018-05-22 23:46:57 +02:00
function isEmittedFile ( file : string ) : boolean {
2018-01-11 20:52:46 +01:00
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 ) ;
}
2018-05-24 22:18:21 +02:00
// If declarationDir is specified, return if its a file in that directory
if ( options . declarationDir && containsPath ( options . declarationDir , filePath , currentDirectory , ! host . useCaseSensitiveFileNames ( ) ) ) {
return true ;
}
2018-01-30 00:41:21 +01:00
// If --outDir, check if file is in that directory
if ( options . outDir ) {
return containsPath ( options . outDir , filePath , currentDirectory , ! host . useCaseSensitiveFileNames ( ) ) ;
}
2018-09-14 00:05:57 +02:00
if ( fileExtensionIsOneOf ( filePath , supportedJSExtensions ) || fileExtensionIs ( filePath , Extension . Dts ) ) {
2018-01-30 00:41:21 +01:00
// Otherwise just check if sourceFile with the name exists
const filePathWithoutExtension = removeFileExtension ( filePath ) ;
2018-05-24 22:18:21 +02:00
return ! ! getSourceFileByPath ( ( filePathWithoutExtension + Extension . Ts ) as Path ) ||
! ! getSourceFileByPath ( ( filePathWithoutExtension + Extension . Tsx ) as Path ) ;
2018-01-30 00:41:21 +01:00
}
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
2019-02-23 00:32:42 +01:00
/*@internal*/
2018-12-19 01:12:37 +01:00
interface CompilerHostLike {
useCaseSensitiveFileNames ( ) : boolean ;
getCurrentDirectory ( ) : string ;
fileExists ( fileName : string ) : boolean ;
readFile ( fileName : string ) : string | undefined ;
readDirectory ? ( rootDir : string , extensions : ReadonlyArray < string > , excludes : ReadonlyArray < string > | undefined , includes : ReadonlyArray < string > , depth? : number ) : string [ ] ;
trace ? ( s : string ) : void ;
onUnRecoverableConfigFileDiagnostic? : DiagnosticReporter ;
}
2018-05-08 00:12:50 +02:00
/* @internal */
2018-12-19 01:12:37 +01:00
export function parseConfigHostFromCompilerHostLike ( host : CompilerHostLike , directoryStructureHost : DirectoryStructureHost = host ) : ParseConfigFileHost {
2018-05-08 00:12:50 +02:00
return {
2018-12-27 19:36:18 +01:00
fileExists : f = > directoryStructureHost . fileExists ( f ) ,
2018-09-02 21:42:38 +02:00
readDirectory ( root , extensions , excludes , includes , depth ) {
2018-12-19 01:12:37 +01:00
Debug . assertDefined ( directoryStructureHost . readDirectory , "'CompilerHost.readDirectory' must be implemented to correctly process 'projectReferences'" ) ;
return directoryStructureHost . readDirectory ! ( root , extensions , excludes , includes , depth ) ;
2018-09-02 21:42:38 +02:00
} ,
2018-12-27 19:36:18 +01:00
readFile : f = > directoryStructureHost . readFile ( f ) ,
2018-05-08 00:12:50 +02:00
useCaseSensitiveFileNames : host.useCaseSensitiveFileNames ( ) ,
getCurrentDirectory : ( ) = > host . getCurrentDirectory ( ) ,
2019-03-14 18:58:57 +01:00
onUnRecoverableConfigFileDiagnostic : host.onUnRecoverableConfigFileDiagnostic || returnUndefined ,
2018-10-25 19:19:57 +02:00
trace : host.trace ? ( s ) = > host . trace ! ( s ) : undefined
2018-05-08 00:12:50 +02:00
} ;
}
2018-09-14 21:57:40 +02:00
// For backward compatibility
/** @deprecated */ export interface ResolveProjectReferencePathHost {
fileExists ( fileName : string ) : boolean ;
}
2019-02-01 23:46:58 +01:00
/* @internal */
export function createPrependNodes ( projectReferences : ReadonlyArray < ProjectReference > | undefined , getCommandLine : ( ref : ProjectReference , index : number ) = > ParsedCommandLine | undefined , readFile : ( path : string ) = > string | undefined ) {
if ( ! projectReferences ) return emptyArray ;
let nodes : InputFiles [ ] | undefined ;
for ( let i = 0 ; i < projectReferences . length ; i ++ ) {
const ref = projectReferences [ i ] ;
const resolvedRefOpts = getCommandLine ( ref , i ) ;
if ( ref . prepend && resolvedRefOpts && resolvedRefOpts . options ) {
const out = resolvedRefOpts . options . outFile || resolvedRefOpts . options . out ;
// Upstream project didn't have outFile set -- skip (error will have been issued earlier)
if ( ! out ) continue ;
2019-02-28 01:15:09 +01:00
const { jsFilePath , sourceMapFilePath , declarationFilePath , declarationMapPath , buildInfoPath } = getOutputPathsForBundle ( resolvedRefOpts . options , /*forceDtsPaths*/ true ) ;
2019-02-01 23:46:58 +01:00
const node = createInputFiles ( readFile , jsFilePath ! , sourceMapFilePath , declarationFilePath ! , declarationMapPath , buildInfoPath ) ;
( nodes || ( nodes = [ ] ) ) . push ( node ) ;
}
}
return nodes || emptyArray ;
}
2018-05-08 00:12:50 +02:00
/ * *
2018-07-06 00:39:03 +02:00
* Returns the target config filename of a project reference .
* Note : The file might not exist .
2018-05-08 00:12:50 +02:00
* /
2018-09-14 21:57:40 +02:00
export function resolveProjectReferencePath ( ref : ProjectReference ) : ResolvedConfigFileName ;
/** @deprecated */ export function resolveProjectReferencePath ( host : ResolveProjectReferencePathHost , ref : ProjectReference ) : ResolvedConfigFileName ;
export function resolveProjectReferencePath ( hostOrRef : ResolveProjectReferencePathHost | ProjectReference , ref? : ProjectReference ) : ResolvedConfigFileName {
const passedInRef = ref ? ref : hostOrRef as ProjectReference ;
return resolveConfigFileProjectName ( passedInRef . path ) ;
2018-05-08 00:12:50 +02:00
}
2018-09-18 22:16:25 +02:00
function getEmitDeclarationOptionName ( options : CompilerOptions ) {
return options . declaration ? "declaration" : "composite" ;
}
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 ( ) ;
2018-08-15 01:10:32 +02:00
case Extension . Json :
return needResolveJsonModule ( ) ;
2016-10-27 17:08:02 +02:00
}
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
}
2018-08-15 01:10:32 +02:00
function needResolveJsonModule() {
return options . resolveJsonModule ? undefined : Diagnostics . Module_0_was_resolved_to_1_but_resolveJsonModule_is_not_used ;
}
2016-10-13 22:02:22 +02:00
}
2017-07-12 19:13:33 +02:00
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
}