2015-06-12 18:01:48 +02:00
namespace ts {
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 */
2020-12-07 20:53:22 +01:00
export function computeCommonSourceDirectoryOfFilenames ( fileNames : readonly 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 {
2020-06-26 01:03:25 +02:00
const existingDirectories = new Map < string , boolean > ( ) ;
2019-03-26 21:37:51 +01:00
const getCanonicalFileName = createGetCanonicalFileName ( system . useCaseSensitiveFileNames ) ;
2020-10-02 19:56:33 +02:00
const computeHash = maybeBind ( system , system . createHash ) || generateDjb2Hash ;
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
}
2019-10-16 20:31:44 +02:00
if ( ( compilerHost . directoryExists || 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 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" ) ;
2016-02-11 09:38:21 +01:00
2019-10-16 20:31:44 +02:00
// NOTE: If patchWriteFileEnsuringDirectory has been called,
2019-10-18 01:26:43 +02:00
// the system.writeFile will do its own directory creation and
2019-10-16 20:31:44 +02:00
// the ensureDirectoriesExist call will always be redundant.
2019-10-18 01:26:43 +02:00
writeFileEnsuringDirectories (
fileName ,
data ,
writeByteOrderMark ,
2019-10-22 01:22:10 +02:00
( path , data , writeByteOrderMark ) = > writeFileWorker ( path , data , writeByteOrderMark ) ,
path = > ( compilerHost . createDirectory || system . createDirectory ) ( path ) ,
path = > directoryExists ( path ) ) ;
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 ) ;
}
}
}
2020-07-02 02:00:26 +02:00
let outputFingerprints : ESMap < string , OutputFingerprint > ;
2019-10-16 20:24:24 +02:00
function writeFileWorker ( fileName : string , data : string , writeByteOrderMark : boolean ) {
2020-10-02 19:56:33 +02:00
if ( ! isWatchSet ( options ) || ! system . getModifiedTime ) {
2019-10-16 20:24:24 +02:00
system . writeFile ( fileName , data , writeByteOrderMark ) ;
2019-10-16 20:31:44 +02:00
return ;
}
if ( ! outputFingerprints ) {
2020-06-26 01:03:25 +02:00
outputFingerprints = new Map < string , OutputFingerprint > ( ) ;
2019-10-16 20:24:24 +02:00
}
2019-10-16 20:31:44 +02:00
2020-10-02 19:56:33 +02:00
const hash = computeHash ( data ) ;
2019-10-16 20:31:44 +02:00
const mtimeBefore = system . getModifiedTime ( fileName ) ;
if ( mtimeBefore ) {
const fingerprint = outputFingerprints . get ( fileName ) ;
// If output has not been changed, and the file has no external modification
if ( fingerprint &&
fingerprint . byteOrderMark === writeByteOrderMark &&
fingerprint . hash === hash &&
fingerprint . mtime . getTime ( ) === mtimeBefore . getTime ( ) ) {
return ;
}
}
system . writeFile ( fileName , data , writeByteOrderMark ) ;
const mtimeAfter = system . getModifiedTime ( fileName ) || missingFileModifiedTime ;
outputFingerprints . set ( fileName , {
hash ,
byteOrderMark : writeByteOrderMark ,
mtime : mtimeAfter
} ) ;
2019-10-16 20:24:24 +02:00
}
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 ) ,
2019-03-25 20:37:55 +01:00
createDirectory : d = > system . createDirectory ( d ) ,
createHash : maybeBind ( system , system . createHash )
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 ;
2020-06-26 01:03:25 +02:00
const readFileCache = new Map < string , string | false > ( ) ;
const fileExistsCache = new Map < string , boolean > ( ) ;
const directoryExistsCache = new Map < string , boolean > ( ) ;
const sourceFileCache = new Map < string , SourceFile > ( ) ;
2018-11-21 18:18:03 +01:00
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
} ;
}
2019-08-08 20:30:18 +02:00
export function getPreEmitDiagnostics ( program : Program , sourceFile? : SourceFile , cancellationToken? : CancellationToken ) : readonly Diagnostic [ ] ;
2019-08-19 12:38:58 +02:00
/*@internal*/ export function getPreEmitDiagnostics ( program : BuilderProgram , sourceFile? : SourceFile , cancellationToken? : CancellationToken ) : readonly Diagnostic [ ] ; // eslint-disable-line @typescript-eslint/unified-signatures
2019-08-08 20:30:18 +02:00
export function getPreEmitDiagnostics ( program : Program | BuilderProgram , sourceFile? : SourceFile , cancellationToken? : CancellationToken ) : readonly Diagnostic [ ] {
2019-12-11 03:25:10 +01:00
let diagnostics : Diagnostic [ ] | undefined ;
diagnostics = addRange ( diagnostics , program . getConfigFileParsingDiagnostics ( ) ) ;
diagnostics = addRange ( diagnostics , program . getOptionsDiagnostics ( cancellationToken ) ) ;
diagnostics = addRange ( diagnostics , program . getSyntacticDiagnostics ( sourceFile , cancellationToken ) ) ;
diagnostics = addRange ( diagnostics , program . getGlobalDiagnostics ( cancellationToken ) ) ;
diagnostics = addRange ( diagnostics , program . getSemanticDiagnostics ( sourceFile , cancellationToken ) ) ;
2015-03-20 00:55:07 +01:00
2018-09-18 22:16:25 +02:00
if ( getEmitDeclarations ( program . getCompilerOptions ( ) ) ) {
2019-12-11 03:25:10 +01:00
diagnostics = addRange ( diagnostics , program . getDeclarationDiagnostics ( sourceFile , cancellationToken ) ) ;
2015-03-20 00:55:07 +01:00
}
2019-12-11 03:25:10 +01:00
return sortAndDeduplicateDiagnostics ( diagnostics || emptyArray ) ;
2015-02-05 10:47:29 +01:00
}
2016-07-15 08:02:56 +02:00
export interface FormatDiagnosticsHost {
getCurrentDirectory ( ) : string ;
getCanonicalFileName ( fileName : string ) : string ;
getNewLine ( ) : string ;
}
2019-08-08 20:30:18 +02:00
export function formatDiagnostics ( diagnostics : readonly 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 ;
}
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 ) ;
2021-05-25 00:28:52 +02:00
lineContent = trimStringEnd ( lineContent ) ; // trim from end
2021-02-04 22:25:53 +01:00
lineContent = lineContent . replace ( /\t/g , " " ) ; // convert tabs to single spaces
2018-06-15 19:54:36 +02:00
// 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 ;
}
2019-08-08 20:30:18 +02:00
export function formatDiagnosticsWithColorAndContext ( diagnostics : readonly 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
2020-12-09 01:10:05 +01:00
}
if ( diagnostic . relatedInformation ) {
output += host . getNewLine ( ) ;
for ( const { file , start , length , messageText } of diagnostic . relatedInformation ) {
if ( file ) {
2018-06-15 19:54:36 +02:00
output += host . getNewLine ( ) ;
2020-12-09 01:10:05 +01:00
output += halfIndent + formatLocation ( file , start ! , host ) ; // TODO: GH#18217
output += formatCodeSpan ( file , start ! , length ! , indent , ForegroundColorEscapeSequences . Cyan , host ) ; // TODO: GH#18217
2018-06-15 19:54:36 +02:00
}
2020-12-09 01:10:05 +01:00
output += host . getNewLine ( ) ;
output += indent + flattenDiagnosticMessageText ( messageText , host . getNewLine ( ) ) ;
2018-06-15 19:54:36 +02:00
}
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
}
2019-06-28 01:30:35 +02:00
export function flattenDiagnosticMessageText ( diag : string | DiagnosticMessageChain | undefined , newLine : string , indent = 0 ) : string {
if ( isString ( diag ) ) {
return diag ;
2015-02-05 10:47:29 +01:00
}
2019-06-28 01:30:35 +02:00
else if ( diag === undefined ) {
return "" ;
}
let result = "" ;
if ( indent ) {
result += newLine ;
2015-02-05 10:47:29 +01:00
2019-06-28 01:30:35 +02:00
for ( let i = 0 ; i < indent ; i ++ ) {
result += " " ;
}
}
result += diag . messageText ;
indent ++ ;
if ( diag . next ) {
for ( const kid of diag . next ) {
result += flattenDiagnosticMessageText ( kid , newLine , indent ) ;
2015-02-05 10:47:29 +01:00
}
}
2019-06-28 01:30:35 +02:00
return result ;
2015-02-05 10:47:29 +01:00
}
2019-04-24 21:38:25 +02:00
/* @internal */
export 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 [ ] = [ ] ;
2020-06-26 01:03:25 +02:00
const cache = new Map < string , 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 ;
}
2021-09-24 23:25:59 +02:00
/* @internal */
interface SourceFileImportsList {
imports : SourceFile [ "imports" ] ;
module Augmentations : SourceFile [ "moduleAugmentations" ] ;
impliedNodeFormat? : SourceFile [ "impliedNodeFormat" ] ;
} ;
/* @internal */
export function getModeForResolutionAtIndex ( file : SourceFileImportsList , index : number ) {
if ( file . impliedNodeFormat === undefined ) return undefined ;
// we ensure all elements of file.imports and file.moduleAugmentations have the relevant parent pointers set during program setup,
// so it's safe to use them even pre-bind
return getModeForUsageLocation ( file , getModuleNameStringLiteralAt ( file , index ) ) ;
}
/* @internal */
export function getModeForUsageLocation ( file : { impliedNodeFormat? : SourceFile [ "impliedNodeFormat" ] } , usage : StringLiteralLike ) {
if ( file . impliedNodeFormat === undefined ) return undefined ;
if ( file . impliedNodeFormat !== ModuleKind . ESNext ) {
// in cjs files, import call expressions are esm format, otherwise everything is cjs
return isImportCall ( walkUpParenthesizedExpressions ( usage . parent ) ) ? ModuleKind.ESNext : ModuleKind.CommonJS ;
}
// in esm files, import=require statements are cjs format, otherwise everything is esm
// imports are only parent'd up to their containing declaration/expression, so access farther parents with care
const exprParentParent = walkUpParenthesizedExpressions ( usage . parent ) ? . parent ;
return exprParentParent && isImportEqualsDeclaration ( exprParentParent ) ? ModuleKind.CommonJS : ModuleKind.ESNext ;
}
/* @internal */
export function loadWithModeAwareCache < T > ( names : string [ ] , containingFile : SourceFile , containingFileName : string , redirectedReference : ResolvedProjectReference | undefined , loader : ( name : string , resolverMode : ModuleKind.CommonJS | ModuleKind . ESNext | undefined , containingFileName : string , redirectedReference : ResolvedProjectReference | undefined ) = > T ) : T [ ] {
if ( names . length === 0 ) {
return [ ] ;
}
const resolutions : T [ ] = [ ] ;
const cache = new Map < string , T > ( ) ;
let i = 0 ;
for ( const name of names ) {
let result : T ;
const mode = getModeForResolutionAtIndex ( containingFile , i ) ;
i ++ ;
const cacheKey = mode !== undefined ? ` ${ mode } | ${ name } ` : name ;
if ( cache . has ( cacheKey ) ) {
result = cache . get ( cacheKey ) ! ;
}
else {
cache . set ( cacheKey , result = loader ( name , mode , containingFileName , redirectedReference ) ) ;
}
resolutions . push ( result ) ;
}
return resolutions ;
}
2020-10-19 21:59:59 +02:00
/* @internal */
export function forEachResolvedProjectReference < T > (
resolvedProjectReferences : readonly ( ResolvedProjectReference | undefined ) [ ] | undefined ,
cb : ( resolvedProjectReference : ResolvedProjectReference , parent : ResolvedProjectReference | undefined ) = > T | undefined
) : T | undefined {
return forEachProjectReference ( /*projectReferences*/ undefined , resolvedProjectReferences , ( resolvedRef , parent ) = > resolvedRef && cb ( resolvedRef , parent ) ) ;
}
function forEachProjectReference < T > (
projectReferences : readonly ProjectReference [ ] | undefined ,
resolvedProjectReferences : readonly ( ResolvedProjectReference | undefined ) [ ] | undefined ,
cbResolvedRef : ( resolvedRef : ResolvedProjectReference | undefined , parent : ResolvedProjectReference | undefined , index : number ) = > T | undefined ,
cbRef ? : ( projectReferences : readonly ProjectReference [ ] | undefined , parent : ResolvedProjectReference | undefined ) = > T | undefined
) : T | undefined {
let seenResolvedRefs : Set < Path > | undefined ;
return worker ( projectReferences , resolvedProjectReferences , /*parent*/ undefined ) ;
function worker (
projectReferences : readonly ProjectReference [ ] | undefined ,
resolvedProjectReferences : readonly ( ResolvedProjectReference | undefined ) [ ] | undefined ,
parent : ResolvedProjectReference | undefined ,
) : T | undefined {
// Visit project references first
if ( cbRef ) {
const result = cbRef ( projectReferences , parent ) ;
2021-08-16 22:53:51 +02:00
if ( result ) return result ;
2020-10-19 21:59:59 +02:00
}
return forEach ( resolvedProjectReferences , ( resolvedRef , index ) = > {
if ( resolvedRef && seenResolvedRefs ? . has ( resolvedRef . sourceFile . path ) ) {
// ignore recursives
return undefined ;
}
const result = cbResolvedRef ( resolvedRef , parent , index ) ;
if ( result || ! resolvedRef ) return result ;
( seenResolvedRefs || = new Set ( ) ) . add ( resolvedRef . sourceFile . path ) ;
return worker ( resolvedRef . commandLine . projectReferences , resolvedRef . references , resolvedRef ) ;
} ) ;
}
}
2019-09-30 18:58:33 +02:00
/* @internal */
export const inferredTypesContainingFile = "__inferred type names__.ts" ;
2018-05-22 20:01:18 +02:00
interface DiagnosticCache < T extends Diagnostic > {
2020-07-02 02:00:26 +02:00
perFile? : ESMap < Path , readonly T [ ] > ;
2019-12-11 03:25:10 +01:00
allDiagnostics? : readonly T [ ] ;
2017-03-07 22:26:41 +01:00
}
2020-12-09 01:10:05 +01:00
/*@internal*/
export function isReferencedFile ( reason : FileIncludeReason | undefined ) : reason is ReferencedFile {
switch ( reason ? . kind ) {
case FileIncludeKind . Import :
case FileIncludeKind . ReferenceFile :
case FileIncludeKind . TypeReferenceDirective :
case FileIncludeKind . LibReferenceDirective :
return true ;
default :
return false ;
}
}
/*@internal*/
export interface ReferenceFileLocation {
file : SourceFile ;
pos : number ;
end : number ;
packageId : PackageId | undefined ;
}
/*@internal*/
export interface SyntheticReferenceFileLocation {
2019-08-09 20:15:20 +02:00
file : SourceFile ;
2020-12-09 01:10:05 +01:00
packageId : PackageId | undefined ;
text : string ;
}
/*@internal*/
export function isReferenceFileLocation ( location : ReferenceFileLocation | SyntheticReferenceFileLocation ) : location is ReferenceFileLocation {
return ( location as ReferenceFileLocation ) . pos !== undefined ;
}
/*@internal*/
export function getReferencedFileLocation ( getSourceFileByPath : ( path : Path ) = > SourceFile | undefined , ref : ReferencedFile ) : ReferenceFileLocation | SyntheticReferenceFileLocation {
const file = Debug . checkDefined ( getSourceFileByPath ( ref . file ) ) ;
const { kind , index } = ref ;
let pos : number | undefined , end : number | undefined , packageId : PackageId | undefined ;
switch ( kind ) {
case FileIncludeKind . Import :
const importLiteral = getModuleNameStringLiteralAt ( file , index ) ;
2021-09-24 23:25:59 +02:00
packageId = file . resolvedModules ? . get ( importLiteral . text , getModeForResolutionAtIndex ( file , index ) ) ? . packageId ;
2020-12-09 01:10:05 +01:00
if ( importLiteral . pos === - 1 ) return { file , packageId , text : importLiteral.text } ;
pos = skipTrivia ( file . text , importLiteral . pos ) ;
end = importLiteral . end ;
break ;
case FileIncludeKind . ReferenceFile :
( { pos , end } = file . referencedFiles [ index ] ) ;
break ;
case FileIncludeKind . TypeReferenceDirective :
( { pos , end } = file . typeReferenceDirectives [ index ] ) ;
2021-10-15 02:00:55 +02:00
packageId = file . resolvedTypeReferenceDirectiveNames ? . get ( toFileNameLowerCase ( file . typeReferenceDirectives [ index ] . fileName ) , file . impliedNodeFormat ) ? . packageId ;
2020-12-09 01:10:05 +01:00
break ;
case FileIncludeKind . LibReferenceDirective :
( { pos , end } = file . libReferenceDirectives [ index ] ) ;
break ;
default :
return Debug . assertNever ( kind ) ;
}
return { file , pos , end , packageId } ;
2019-08-09 20:15:20 +02:00
}
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 ,
2020-02-26 01:11:21 +01:00
getSourceVersion : ( path : Path , fileName : string ) = > string | undefined ,
2017-08-30 20:49:58 +02:00
fileExists : ( fileName : string ) = > boolean ,
hasInvalidatedResolution : HasInvalidatedResolution ,
2020-06-09 21:00:37 +02:00
hasChangedAutomaticTypeDirectiveNames : HasChangedAutomaticTypeDirectiveNames | undefined ,
2021-03-26 21:23:03 +01:00
getParsedCommandLine : ( fileName : string ) = > ParsedCommandLine | undefined ,
2019-08-08 20:30:18 +02:00
projectReferences : readonly 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
2021-03-26 21:23:03 +01:00
if ( ! program || hasChangedAutomaticTypeDirectiveNames ? . ( ) ) return false ;
2017-07-25 01:57:49 +02:00
2020-01-21 20:26:17 +01:00
// If root file names don't match
2021-03-26 21:23:03 +01:00
if ( ! arrayIsEqualTo ( program . getRootFileNames ( ) , rootFileNames ) ) return false ;
2017-07-25 01:57:49 +02:00
2018-09-19 01:21:05 +02:00
let seenResolvedRefs : ResolvedProjectReference [ ] | undefined ;
2020-01-21 20:26:17 +01:00
// If project references don't match
2021-03-26 21:23:03 +01:00
if ( ! arrayIsEqualTo ( program . getProjectReferences ( ) , projectReferences , projectReferenceUptoDate ) ) return false ;
2018-09-14 00:18:53 +02:00
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
2021-03-26 21:23:03 +01:00
if ( program . getSourceFiles ( ) . some ( sourceFileNotUptoDate ) ) 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
2021-03-26 21:23:03 +01:00
if ( program . getMissingFilePaths ( ) . some ( fileExists ) ) return false ;
2017-07-27 00:12:03 +02:00
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
2021-03-26 21:23:03 +01:00
if ( ! compareDataObjects ( currentOptions , newOptions ) ) return false ;
2017-07-25 01:57:49 +02:00
// If everything matches but the text of config file is changed,
// error locations can change for program options, so update the program
2021-03-26 21:23:03 +01:00
if ( currentOptions . configFile && newOptions . configFile ) return currentOptions . configFile . text === newOptions . configFile . text ;
2017-07-25 01:57:49 +02:00
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 ) {
2020-02-26 01:11:21 +01:00
return sourceFile . version === getSourceVersion ( sourceFile . resolvedPath , sourceFile . fileName ) ;
2018-09-15 02:12:22 +02:00
}
function projectReferenceUptoDate ( oldRef : ProjectReference , newRef : ProjectReference , index : number ) {
2021-03-26 21:23:03 +01:00
return projectReferenceIsEqualTo ( oldRef , newRef ) &&
resolvedProjectReferenceUptoDate ( program ! . getResolvedProjectReferences ( ) ! [ index ] , oldRef ) ;
2018-09-19 01:21:05 +02:00
}
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
// Assume true
2021-03-26 21:23:03 +01:00
if ( contains ( seenResolvedRefs , oldResolvedRef ) ) return true ;
2018-09-19 01:21:05 +02:00
2021-03-26 21:23:03 +01:00
const refPath = resolveProjectReferencePath ( oldRef ) ;
const newParsedCommandLine = getParsedCommandLine ( refPath ) ;
// Check if config file exists
if ( ! newParsedCommandLine ) return false ;
// If change in source file
if ( oldResolvedRef . commandLine . options . configFile !== newParsedCommandLine . options . configFile ) return false ;
// check file names
if ( ! arrayIsEqualTo ( oldResolvedRef . commandLine . fileNames , newParsedCommandLine . fileNames ) ) return false ;
2018-09-19 01:21:05 +02:00
// 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.
2021-03-26 21:23:03 +01:00
const refPath = resolveProjectReferencePath ( oldRef ) ;
return ! getParsedCommandLine ( refPath ) ;
2017-07-25 01:57:49 +02:00
}
}
2019-08-08 20:30:18 +02:00
export function getConfigFileParsingDiagnostics ( configFileParseResult : ParsedCommandLine ) : readonly Diagnostic [ ] {
2018-03-15 22:32:01 +01:00
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 ;
}
2021-09-24 23:25:59 +02:00
/ * *
* A function for determining if a given file is esm or cjs format , assuming modern node module resolution rules , as configured by the
* ` options ` parameter .
*
* @param fileName The normalized absolute path to check the format of ( it need not exist on disk )
* @param [ packageJsonInfoCache ] A cache for package file lookups - it ' s best to have a cache when this function is called often
* @param host The ModuleResolutionHost which can perform the filesystem lookups for package json data
* @param options The compiler options to perform the analysis under - relevant options are ` moduleResolution ` and ` traceResolution `
* @returns ` undefined ` if the path has no relevant implied format , ` ModuleKind.ESNext ` for esm format , and ` ModuleKind.CommonJS ` for cjs format
* /
export function getImpliedNodeFormatForFile ( fileName : Path , packageJsonInfoCache : PackageJsonInfoCache | undefined , host : ModuleResolutionHost , options : CompilerOptions ) : ModuleKind . ESNext | ModuleKind . CommonJS | undefined {
switch ( getEmitModuleResolutionKind ( options ) ) {
case ModuleResolutionKind . Node12 :
case ModuleResolutionKind . NodeNext :
return fileExtensionIsOneOf ( fileName , [ Extension . Dmts , Extension . Mts , Extension . Mjs ] ) ? ModuleKind . ESNext :
fileExtensionIsOneOf ( fileName , [ Extension . Dcts , Extension . Cts , Extension . Cjs ] ) ? ModuleKind . CommonJS :
fileExtensionIsOneOf ( fileName , [ Extension . Dts , Extension . Ts , Extension . Tsx , Extension . Js , Extension . Jsx ] ) ? lookupFromPackageJson ( ) :
undefined ; // other extensions, like `json` or `tsbuildinfo`, are set as `undefined` here but they should never be fed through the transformer pipeline
default :
return undefined ;
}
function lookupFromPackageJson ( ) : ModuleKind . ESNext | ModuleKind . CommonJS {
const scope = getPackageScopeForPath ( fileName , packageJsonInfoCache , host , options ) ;
return scope ? . packageJsonContent . type === "module" ? ModuleKind.ESNext : ModuleKind.CommonJS ;
}
}
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`.
2021-05-27 00:57:43 +02:00
return optionsHaveChanges ( program . getCompilerOptions ( ) , newOptions , sourceFileAffectingCompilerOptions ) ;
2017-07-25 01:57:49 +02:00
}
2019-08-08 20:30:18 +02:00
function createCreateProgramOptions ( rootNames : readonly string [ ] , options : CompilerOptions , host? : CompilerHost , oldProgram? : Program , configFileParsingDiagnostics? : readonly Diagnostic [ ] ) : CreateProgramOptions {
2018-05-08 00:12:50 +02:00
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 .
* /
2019-08-08 20:30:18 +02:00
export function createProgram ( rootNames : readonly string [ ] , options : CompilerOptions , host? : CompilerHost , oldProgram? : Program , configFileParsingDiagnostics? : readonly Diagnostic [ ] ) : Program ;
export function createProgram ( rootNamesOrOptions : readonly string [ ] | CreateProgramOptions , _options? : CompilerOptions , _host? : CompilerHost , _oldProgram? : Program , _configFileParsingDiagnostics? : readonly 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
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 [ ] ;
2020-07-22 22:53:30 +02:00
let symlinks : SymlinkCache | undefined ;
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 ;
2020-06-26 01:03:25 +02:00
let classifiableNames : Set < __String > ;
const ambientModuleNameToUnmodifiedFileName = new Map < string , string > ( ) ;
2020-12-09 01:10:05 +01:00
let fileReasons = createMultiMap < Path , FileIncludeReason > ( ) ;
2019-12-11 03:25:10 +01:00
const cachedBindAndCheckDiagnosticsForFile : DiagnosticCache < Diagnostic > = { } ;
2018-05-22 20:01:18 +02:00
const cachedDeclarationDiagnosticsForFile : DiagnosticCache < DiagnosticWithLocation > = { } ;
2017-03-07 22:26:41 +01:00
2020-06-26 01:03:25 +02:00
let resolvedTypeReferenceDirectives = new Map < string , ResolvedTypeReferenceDirective | undefined > ( ) ;
2020-12-09 01:10:05 +01:00
let fileProcessingDiagnostics : FilePreprocessingDiagnostics [ ] | undefined ;
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.
2020-06-26 01:03:25 +02:00
const module sWithElidedImports = new Map < string , 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.
2020-06-26 01:03:25 +02:00
const sourceFilesFoundSearchingNodeModules = new Map < string , boolean > ( ) ;
2016-06-27 05:48:22 +02:00
2021-01-13 22:01:53 +01:00
tracing ? . push ( tracing . Phase . Program , "createProgram" , { configFilePath : options.configFilePath , rootDir : options.rootDir } , /*separateBeginAndEnd*/ true ) ;
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 ) ;
2021-09-24 23:25:59 +02:00
const supportedExtensionsWithJsonIfResolveJsonModule = getSupportedExtensionsWithJsonIfResolveJsonModule ( 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
2020-06-26 01:03:25 +02:00
const hasEmitBlockingDiagnostics = new Map < string , boolean > ( ) ;
2020-12-09 01:10:05 +01:00
let _compilerOptionsObjectLiteralSyntax : ObjectLiteralExpression | false | undefined ;
2015-10-01 01:10:52 +02:00
2018-05-22 23:46:57 +02:00
let module ResolutionCache : ModuleResolutionCache | undefined ;
2021-04-22 06:30:18 +02:00
let typeReferenceDirectiveResolutionCache : TypeReferenceDirectiveResolutionCache | undefined ;
2021-09-24 23:25:59 +02:00
let actualResolveModuleNamesWorker : ( module Names : string [ ] , containingFile : SourceFile , containingFileName : 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 ) {
2021-09-24 23:25:59 +02:00
actualResolveModuleNamesWorker = ( module Names , containingFile , containingFileName , reusedNames , redirectedReference ) = > host . resolveModuleNames ! ( Debug . checkEachDefined ( module Names ) , containingFileName , reusedNames , redirectedReference , options , containingFile ) . 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
} ) ;
2021-09-24 23:25:59 +02:00
module ResolutionCache = host . getModuleResolutionCache ? . ( ) ;
2016-04-01 21:41:01 +02:00
}
else {
2021-04-22 06:30:18 +02:00
module ResolutionCache = createModuleResolutionCache ( currentDirectory , getCanonicalFileName , options ) ;
2021-09-24 23:25:59 +02:00
const loader = ( module Name : string , resolverMode : ModuleKind.CommonJS | ModuleKind . ESNext | undefined , containingFileName : string , redirectedReference : ResolvedProjectReference | undefined ) = > resolveModuleName ( module Name , containingFileName , options , host , module ResolutionCache , redirectedReference , resolverMode ) . resolvedModule ! ; // TODO: GH#18217
actualResolveModuleNamesWorker = ( module Names , containingFile , containingFileName , _reusedNames , redirectedReference ) = > loadWithModeAwareCache < ResolvedModuleFull > ( Debug . checkEachDefined ( module Names ) , containingFile , containingFileName , redirectedReference , loader ) ;
2016-04-01 21:41:01 +02:00
}
2020-02-27 19:22:40 +01:00
let actualResolveTypeReferenceDirectiveNamesWorker : ( typeDirectiveNames : string [ ] , containingFile : string , redirectedReference? : ResolvedProjectReference ) = > ( ResolvedTypeReferenceDirective | undefined ) [ ] ;
2016-04-01 21:41:01 +02:00
if ( host . resolveTypeReferenceDirectives ) {
2020-02-27 19:22:40 +01:00
actualResolveTypeReferenceDirectiveNamesWorker = ( typeDirectiveNames , containingFile , redirectedReference ) = > host . resolveTypeReferenceDirectives ! ( Debug . checkEachDefined ( typeDirectiveNames ) , containingFile , redirectedReference , options ) ;
2016-04-01 21:41:01 +02:00
}
else {
2021-04-22 06:30:18 +02:00
typeReferenceDirectiveResolutionCache = createTypeReferenceDirectiveResolutionCache ( currentDirectory , getCanonicalFileName , /*options*/ undefined , module ResolutionCache?.getPackageJsonInfoCache ( ) ) ;
const loader = ( typesRef : string , containingFile : string , redirectedReference : ResolvedProjectReference | undefined ) = > resolveTypeReferenceDirective (
typesRef ,
containingFile ,
options ,
host ,
redirectedReference ,
typeReferenceDirectiveResolutionCache ,
) . resolvedTypeReferenceDirective ! ; // TODO: GH#18217
2020-02-27 19:22:40 +01:00
actualResolveTypeReferenceDirectiveNamesWorker = ( typeReferenceDirectiveNames , containingFile , redirectedReference ) = > loadWithLocalCache < ResolvedTypeReferenceDirective > ( Debug . checkEachDefined ( 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.
2020-06-26 01:03:25 +02:00
const packageIdToSourceFile = new Map < string , SourceFile > ( ) ;
2017-08-09 23:39:06 +02:00
// Maps from a SourceFile's `.path` to the name of the package it was imported with.
2021-05-27 01:50:14 +02:00
let sourceFileToPackageName = new Map < Path , 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.
2021-05-27 01:50:14 +02:00
let redirectTargetsMap = createMultiMap < Path , string > ( ) ;
2021-07-19 18:56:24 +02:00
let usesUriStyleNodeCoreModules = false ;
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
* /
2020-06-26 01:03:25 +02:00
const filesByName = new Map < string , SourceFile | false | undefined > ( ) ;
2019-08-08 20:30:18 +02:00
let missingFilePaths : readonly 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
2020-06-26 01:03:25 +02:00
const filesByNameIgnoreCase = host . useCaseSensitiveFileNames ( ) ? new Map < string , 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
2019-08-08 20:30:18 +02:00
let resolvedProjectReferences : readonly ( ResolvedProjectReference | undefined ) [ ] | undefined ;
2020-07-02 02:00:26 +02:00
let projectReferenceRedirects : ESMap < Path , ResolvedProjectReference | false > | undefined ;
let mapFromFileToProjectReferenceRedirects : ESMap < Path , Path > | undefined ;
let mapFromToProjectReferenceRedirectSource : ESMap < Path , SourceOfProjectReferenceRedirect > | undefined ;
2020-03-12 21:11:11 +01:00
const useSourceOfProjectReferenceRedirect = ! ! host . useSourceOfProjectReferenceRedirect ? . ( ) &&
! options . disableSourceOfProjectReferenceRedirect ;
2020-07-22 22:53:30 +02:00
const { onProgramCreateComplete , fileExists , directoryExists } = updateHostForUseSourceOfProjectReferenceRedirect ( {
2020-03-12 21:11:11 +01:00
compilerHost : host ,
2020-07-22 22:53:30 +02:00
getSymlinkCache ,
2020-03-12 21:11:11 +01:00
useSourceOfProjectReferenceRedirect ,
toPath ,
getResolvedProjectReferences ,
getSourceOfProjectReferenceRedirect ,
forEachResolvedProjectReference
} ) ;
2021-09-24 23:25:59 +02:00
const readFile = host . readFile . bind ( host ) as typeof host . readFile ;
2018-06-06 01:28:42 +02:00
2021-01-13 22:01:53 +01:00
tracing ? . push ( tracing . Phase . Program , "shouldProgramCreateNewSourceFiles" , { hasOldProgram : ! ! oldProgram } ) ;
2017-07-25 01:57:49 +02:00
const shouldCreateNewSourceFile = shouldProgramCreateNewSourceFiles ( oldProgram , options ) ;
2021-01-13 22:01:53 +01:00
tracing ? . pop ( ) ;
2019-08-01 23:23:57 +02:00
// We set `structuralIsReused` to `undefined` because `tryReuseStructureFromOldProgram` calls `tryReuseStructureFromOldProgram` which checks
// `structuralIsReused`, which would be a TDZ violation if it was not set in advance to `undefined`.
2020-10-10 00:56:51 +02:00
let structureIsReused : StructureIsReused ;
2021-01-13 22:01:53 +01:00
tracing ? . push ( tracing . Phase . Program , "tryReuseStructureFromOldProgram" , { } ) ;
2020-10-10 00:56:51 +02:00
structureIsReused = tryReuseStructureFromOldProgram ( ) ; // eslint-disable-line prefer-const
2021-01-13 22:01:53 +01:00
tracing ? . pop ( ) ;
2020-10-10 00:56:51 +02:00
if ( structureIsReused !== 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 ) {
2020-12-09 01:10:05 +01:00
resolvedProjectReferences ? . forEach ( ( parsedRef , index ) = > {
if ( ! parsedRef ) return ;
2020-06-02 20:49:21 +02:00
const out = outFile ( parsedRef . commandLine . options ) ;
2019-07-11 00:21:24 +02:00
if ( useSourceOfProjectReferenceRedirect ) {
2019-06-21 22:11:39 +02:00
if ( out || getEmitModuleKind ( parsedRef . commandLine . options ) === ModuleKind . None ) {
for ( const fileName of parsedRef . commandLine . fileNames ) {
2020-12-09 01:10:05 +01:00
processProjectReferenceFile ( fileName , { kind : FileIncludeKind.SourceFromProjectReference , index } ) ;
2019-06-21 22:11:39 +02:00
}
}
2019-03-08 19:19:02 +01:00
}
2019-06-21 22:11:39 +02:00
else {
if ( out ) {
2020-12-09 01:10:05 +01:00
processProjectReferenceFile ( changeExtension ( out , ".d.ts" ) , { kind : FileIncludeKind.OutputFromProjectReference , index } ) ;
2019-06-21 22:11:39 +02:00
}
else if ( getEmitModuleKind ( parsedRef . commandLine . options ) === ModuleKind . None ) {
2020-12-07 20:53:22 +01:00
const getCommonSourceDirectory = memoize ( ( ) = > getCommonSourceDirectoryOfConfig ( parsedRef . commandLine , ! host . useCaseSensitiveFileNames ( ) ) ) ;
2019-06-21 22:11:39 +02:00
for ( const fileName of parsedRef . commandLine . fileNames ) {
2020-01-08 18:54:32 +01:00
if ( ! fileExtensionIs ( fileName , Extension . Dts ) && ! fileExtensionIs ( fileName , Extension . Json ) ) {
2020-12-09 01:10:05 +01:00
processProjectReferenceFile ( getOutputDeclarationFileName ( fileName , parsedRef . commandLine , ! host . useCaseSensitiveFileNames ( ) , getCommonSourceDirectory ) , { kind : FileIncludeKind.OutputFromProjectReference , index } ) ;
2019-06-21 22:11:39 +02:00
}
2019-03-08 19:19:02 +01:00
}
2018-10-31 23:03:42 +01:00
}
2018-06-06 01:20:07 +02:00
}
2020-12-09 01:10:05 +01:00
} ) ;
2018-06-06 01:20:07 +02:00
}
}
2021-01-13 22:01:53 +01:00
tracing ? . push ( tracing . Phase . Program , "processRootFiles" , { count : rootNames.length } ) ;
2020-12-09 01:10:05 +01:00
forEach ( rootNames , ( name , index ) = > processRootFile ( name , /*isDefaultLib*/ false , /*ignoreNoDefaultLib*/ false , { kind : FileIncludeKind.RootFile , index } ) ) ;
2021-01-13 22:01:53 +01:00
tracing ? . pop ( ) ;
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 ) {
2021-01-13 22:01:53 +01:00
tracing ? . push ( tracing . Phase . Program , "processTypeReferences" , { count : 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 ( ) ;
2019-09-30 18:58:33 +02:00
const containingFilename = combinePaths ( containingDirectory , inferredTypesContainingFile ) ;
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 ++ ) {
2020-12-09 01:10:05 +01:00
processTypeReferenceDirective ( typeReferences [ i ] , resolutions [ i ] , { kind : FileIncludeKind.AutomaticTypeDirectiveFile , typeReference : typeReferences [ i ] , packageId : resolutions [ i ] ? . packageId } ) ;
2016-04-06 22:49:25 +02:00
}
2021-01-13 22:01:53 +01:00
tracing ? . pop ( ) ;
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 ) {
2020-12-09 01:10:05 +01:00
processRootFile ( defaultLibraryFileName , /*isDefaultLib*/ true , /*ignoreNoDefaultLib*/ false , { kind : FileIncludeKind.LibFile } ) ;
2016-03-28 23:20:29 +02:00
}
else {
2020-12-09 01:10:05 +01:00
forEach ( options . lib , ( libFileName , index ) = > {
2021-09-15 22:25:08 +02:00
processRootFile ( pathForLibFile ( libFileName ) , /*isDefaultLib*/ true , /*ignoreNoDefaultLib*/ false , { kind : FileIncludeKind.LibFile , index } ) ;
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 ||
2021-01-19 18:13:26 +01:00
// old file wasn't redirect but new file is
2018-10-02 23:10:19 +02:00
( oldSourceFile . resolvedPath === oldSourceFile . path && newFile . resolvedPath !== oldSourceFile . path ) ) {
host . onReleaseOldSourceFile ( oldSourceFile , oldProgram . getCompilerOptions ( ) , ! ! getSourceFileByPath ( oldSourceFile . path ) ) ;
2017-07-25 01:57:49 +02:00
}
}
2021-03-26 21:23:03 +01:00
if ( ! host . getParsedCommandLine ) {
oldProgram . forEachResolvedProjectReference ( resolvedProjectReference = > {
if ( ! getResolvedProjectReferenceByPath ( resolvedProjectReference . sourceFile . path ) ) {
host . onReleaseOldSourceFile ! ( resolvedProjectReference . sourceFile , oldProgram ! . getCompilerOptions ( ) , /*hasSourceFileByPath*/ false ) ;
}
} ) ;
}
}
// Release commandlines that new program does not use
if ( oldProgram && host . onReleaseParsedCommandLine ) {
forEachProjectReference (
oldProgram . getProjectReferences ( ) ,
oldProgram . getResolvedProjectReferences ( ) ,
( oldResolvedRef , parent , index ) = > {
const oldReference = parent ? . commandLine . projectReferences ! [ index ] || oldProgram ! . getProjectReferences ( ) ! [ index ] ;
const oldRefPath = resolveProjectReferencePath ( oldReference ) ;
if ( ! projectReferenceRedirects ? . has ( toPath ( oldRefPath ) ) ) {
host . onReleaseParsedCommandLine ! ( oldRefPath , oldResolvedRef , oldProgram ! . getCompilerOptions ( ) ) ;
}
2018-10-02 23:10:19 +02:00
}
2021-03-26 21:23:03 +01:00
) ;
2017-07-25 01:57:49 +02:00
}
2021-04-22 06:30:18 +02:00
typeReferenceDirectiveResolutionCache = undefined ;
2015-07-09 23:45:39 +02:00
// unconditionally set oldProgram to undefined to prevent it from being captured in closure
oldProgram = undefined ;
2019-06-19 16:58:49 +02:00
const program : 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
2021-07-13 02:23:08 +02:00
getModuleResolutionCache : ( ) = > module ResolutionCache ,
2019-12-20 16:44:35 +01:00
getFilesByNameMap : ( ) = > filesByName ,
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 ,
2020-10-10 02:25:04 +02:00
getCachedSemanticDiagnostics ,
2018-05-31 19:47:17 +02:00
getSuggestionDiagnostics ,
2015-02-04 23:29:25 +01:00
getDeclarationDiagnostics ,
2019-12-11 03:25:10 +01:00
getBindAndCheckDiagnostics ,
getProgramDiagnostics ,
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 ( ) ,
2020-02-26 21:55:28 +01:00
getInstantiationCount : ( ) = > getDiagnosticsProducingTypeChecker ( ) . getInstantiationCount ( ) ,
2019-04-17 23:32:18 +02:00
getRelationCacheSizes : ( ) = > getDiagnosticsProducingTypeChecker ( ) . getRelationCacheSizes ( ) ,
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 ,
2021-07-19 18:56:24 +02:00
usesUriStyleNodeCoreModules ,
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 ,
2019-07-01 23:29:32 +02:00
isSourceOfProjectReferenceRedirect ,
2019-09-27 22:38:31 +02:00
emitBuildInfo ,
2020-03-20 00:53:44 +01:00
fileExists ,
2021-09-24 23:25:59 +02:00
readFile ,
2020-07-22 22:53:30 +02:00
directoryExists ,
getSymlinkCache ,
realpath : host.realpath?.bind ( host ) ,
2020-03-03 19:50:01 +01:00
useCaseSensitiveFileNames : ( ) = > host . useCaseSensitiveFileNames ( ) ,
2020-12-09 01:10:05 +01:00
getFileIncludeReasons : ( ) = > fileReasons ,
2020-10-10 00:56:51 +02:00
structureIsReused ,
2014-12-16 22:14:14 +01:00
} ;
2015-10-12 21:25:13 +02:00
2020-03-12 21:11:11 +01:00
onProgramCreateComplete ( ) ;
2020-12-09 01:10:05 +01:00
// Add file processingDiagnostics
fileProcessingDiagnostics ? . forEach ( diagnostic = > {
switch ( diagnostic . kind ) {
case FilePreprocessingDiagnosticsKind . FilePreprocessingFileExplainingDiagnostic :
return programDiagnostics . add ( createDiagnosticExplainingFile ( diagnostic . file && getSourceFileByPath ( diagnostic . file ) , diagnostic . fileProcessingReason , diagnostic . diagnostic , diagnostic . args || emptyArray ) ) ;
case FilePreprocessingDiagnosticsKind . FilePreprocessingReferencedDiagnostic :
const { file , pos , end } = getReferencedFileLocation ( getSourceFileByPath , diagnostic . reason ) as ReferenceFileLocation ;
return programDiagnostics . add ( createFileDiagnostic ( file , Debug . checkDefined ( pos ) , Debug . checkDefined ( end ) - pos , diagnostic . diagnostic , . . . diagnostic . args || emptyArray ) ) ;
default :
Debug . assertNever ( diagnostic ) ;
}
} ) ;
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" ) ;
2021-01-13 22:01:53 +01:00
tracing ? . pop ( ) ;
2015-10-12 22:10:54 +02:00
2014-12-16 22:14:14 +01:00
return program ;
2020-10-06 20:23:05 +02:00
function resolveModuleNamesWorker ( module Names : string [ ] , containingFile : SourceFile , reusedNames : string [ ] | undefined ) : readonly ResolvedModuleFull [ ] {
if ( ! module Names.length ) return emptyArray ;
const containingFileName = getNormalizedAbsolutePath ( containingFile . originalFileName , currentDirectory ) ;
const redirectedReference = getRedirectReferenceForResolution ( containingFile ) ;
2021-01-13 22:01:53 +01:00
tracing ? . push ( tracing . Phase . Program , "resolveModuleNamesWorker" , { containingFileName } ) ;
2020-02-27 19:22:40 +01:00
performance . mark ( "beforeResolveModule" ) ;
2021-09-24 23:25:59 +02:00
const result = actualResolveModuleNamesWorker ( module Names , containingFile , containingFileName , reusedNames , redirectedReference ) ;
2020-02-27 19:22:40 +01:00
performance . mark ( "afterResolveModule" ) ;
performance . measure ( "ResolveModule" , "beforeResolveModule" , "afterResolveModule" ) ;
2021-01-13 22:01:53 +01:00
tracing ? . pop ( ) ;
2020-02-27 19:22:40 +01:00
return result ;
}
2020-10-06 20:23:05 +02:00
function resolveTypeReferenceDirectiveNamesWorker ( typeDirectiveNames : string [ ] , containingFile : string | SourceFile ) : readonly ( ResolvedTypeReferenceDirective | undefined ) [ ] {
if ( ! typeDirectiveNames . length ) return [ ] ;
const containingFileName = ! isString ( containingFile ) ? getNormalizedAbsolutePath ( containingFile . originalFileName , currentDirectory ) : containingFile ;
const redirectedReference = ! isString ( containingFile ) ? getRedirectReferenceForResolution ( containingFile ) : undefined ;
2021-01-13 22:01:53 +01:00
tracing ? . push ( tracing . Phase . Program , "resolveTypeReferenceDirectiveNamesWorker" , { containingFileName } ) ;
2020-10-21 19:38:09 +02:00
performance . mark ( "beforeResolveTypeReference" ) ;
2020-10-06 20:23:05 +02:00
const result = actualResolveTypeReferenceDirectiveNamesWorker ( typeDirectiveNames , containingFileName , redirectedReference ) ;
2020-02-27 19:22:40 +01:00
performance . mark ( "afterResolveTypeReference" ) ;
performance . measure ( "ResolveTypeReference" , "beforeResolveTypeReference" , "afterResolveTypeReference" ) ;
2021-01-13 22:01:53 +01:00
tracing ? . pop ( ) ;
2020-02-27 19:22:40 +01:00
return result ;
}
2020-10-06 20:23:05 +02:00
function getRedirectReferenceForResolution ( file : SourceFile ) {
const redirect = getResolvedProjectReferenceToRedirect ( file . originalFileName ) ;
2021-09-24 23:25:59 +02:00
if ( redirect || ! fileExtensionIsOneOf ( file . originalFileName , [ Extension . Dts , Extension . Dcts , Extension . Dmts ] ) ) return redirect ;
2020-10-06 20:23:05 +02:00
// The originalFileName could not be actual source file name if file found was d.ts from referecned project
// So in this case try to look up if this is output from referenced project, if it is use the redirected project in that case
2021-08-27 00:35:04 +02:00
const resultFromDts = getRedirectReferenceForResolutionFromSourceOfProject ( file . path ) ;
2020-10-06 20:23:05 +02:00
if ( resultFromDts ) return resultFromDts ;
// If preserveSymlinks is true, module resolution wont jump the symlink
// but the resolved real path may be the .d.ts from project reference
// Note:: Currently we try the real path only if the
// file is from node_modules to avoid having to run real path on all file paths
if ( ! host . realpath || ! options . preserveSymlinks || ! stringContains ( file . originalFileName , nodeModulesPathPart ) ) return undefined ;
2021-08-27 00:35:04 +02:00
const realDeclarationPath = toPath ( host . realpath ( file . originalFileName ) ) ;
return realDeclarationPath === file . path ? undefined : getRedirectReferenceForResolutionFromSourceOfProject ( realDeclarationPath ) ;
2020-10-06 20:23:05 +02:00
}
2021-08-27 00:35:04 +02:00
function getRedirectReferenceForResolutionFromSourceOfProject ( filePath : Path ) {
const source = getSourceOfProjectReferenceRedirect ( filePath ) ;
2020-10-06 20:23:05 +02:00
if ( isString ( source ) ) return getResolvedProjectReferenceToRedirect ( source ) ;
if ( ! source ) return undefined ;
// Output of .d.ts file so return resolved ref that matches the out file name
return forEachResolvedProjectReference ( resolvedRef = > {
const out = outFile ( resolvedRef . commandLine . options ) ;
if ( ! out ) return undefined ;
return toPath ( out ) === filePath ? resolvedRef : undefined ;
} ) ;
}
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 ;
}
2021-09-24 23:25:59 +02:00
function getResolvedModuleWithFailedLookupLocationsFromCache ( module Name : string , containingFile : string , mode? : ModuleKind.CommonJS | ModuleKind . ESNext ) : ResolvedModuleWithFailedLookupLocations | undefined {
return module ResolutionCache && resolveModuleNameFromCache ( module Name , containingFile , module ResolutionCache , mode ) ;
2018-04-20 22:43:09 +02:00
}
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-11-27 22:47:19 +01:00
const emittedFiles = filter ( files , file = > sourceFileMayBeEmitted ( file , program ) ) ;
2020-12-07 20:53:22 +01:00
commonSourceDirectory = ts . getCommonSourceDirectory (
options ,
( ) = > mapDefined ( emittedFiles , file = > file . isDeclarationFile ? undefined : file . fileName ) ,
currentDirectory ,
getCanonicalFileName ,
commonSourceDirectory = > checkSourceFilesBelongToPath ( emittedFiles , commonSourceDirectory )
) ;
2015-11-19 02:10:22 +01:00
}
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 ( ) ;
2020-06-26 01:03:25 +02:00
classifiableNames = new Set ( ) ;
2016-10-28 00:50:21 +02:00
for ( const sourceFile of files ) {
2020-06-26 01:03:25 +02:00
sourceFile . classifiableNames ? . forEach ( value = > classifiableNames . add ( value ) ) ;
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
}
2020-10-06 20:23:05 +02:00
function resolveModuleNamesReusingOldState ( module Names : string [ ] , file : SourceFile ) : readonly ResolvedModuleFull [ ] {
2020-10-10 00:56:51 +02:00
if ( structureIsReused === 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.
2020-10-06 20:23:05 +02:00
return resolveModuleNamesWorker ( module Names , file , /*reusedNames*/ undefined ) ;
2016-11-02 22:41:26 +01:00
}
2020-10-06 20:23:05 +02:00
const oldSourceFile = oldProgram && oldProgram . getSourceFile ( file . fileName ) ;
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 [ ] = [ ] ;
2021-09-24 23:25:59 +02:00
let i = 0 ;
2017-04-29 01:27:41 +02:00
for ( const module Name of module Names ) {
2021-09-24 23:25:59 +02:00
const resolvedModule = file . resolvedModules . get ( module Name , getModeForResolutionAtIndex ( file , i ) ) ! ;
i ++ ;
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. */
2021-05-18 15:20:57 +02:00
const predictedToResolveToAmbientModuleMarker : ResolvedModuleFull = { } as any ;
2016-11-02 22:41:26 +01:00
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 ) ) {
2021-09-24 23:25:59 +02:00
const oldResolvedModule = getResolvedModule ( oldSourceFile , module Name , getModeForResolutionAtIndex ( oldSourceFile , i ) ) ;
2017-04-28 05:27:48 +02:00
if ( oldResolvedModule ) {
if ( isTraceEnabled ( options , host ) ) {
2021-05-27 20:14:12 +02:00
trace ( host ,
oldResolvedModule . packageId ?
Diagnostics . Reusing_resolution_of_module_0_from_1_of_old_program_it_was_successfully_resolved_to_2_with_Package_ID_3 :
Diagnostics . Reusing_resolution_of_module_0_from_1_of_old_program_it_was_successfully_resolved_to_2 ,
module Name ,
getNormalizedAbsolutePath ( file . originalFileName , currentDirectory ) ,
oldResolvedModule . resolvedFileName ,
oldResolvedModule . packageId && packageIdToString ( oldResolvedModule . packageId )
) ;
2017-04-28 05:27:48 +02:00
}
( 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 ) ) {
2020-10-06 20:23:05 +02:00
trace ( host , Diagnostics . Module_0_was_resolved_as_locally_declared_ambient_module_in_file_1 , module Name , getNormalizedAbsolutePath ( file . originalFileName , currentDirectory ) ) ;
2016-11-02 22:41:26 +01:00
}
}
else {
2021-09-24 23:25:59 +02:00
resolvesToAmbientModuleInNonModifiedFile = module NameResolvesToAmbientModuleInNonModifiedFile ( module Name , i ) ;
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
2020-10-06 20:23:05 +02:00
? resolveModuleNamesWorker ( unknownModuleNames , file , reusedNames )
2016-11-02 22:41:26 +01:00
: emptyArray ;
2017-04-28 05:27:48 +02:00
// Combine results of resolutions and predicted results
if ( ! result ) {
// There were no unresolved/ambient resolutions.
Debug . assert ( resolutions . length === module Names.length ) ;
2018-02-17 03:38:00 +01:00
return resolutions ;
2017-04-28 05:27:48 +02:00
}
2016-11-02 22:41:26 +01:00
let j = 0 ;
for ( let i = 0 ; i < result . length ; i ++ ) {
2017-04-28 05:27:48 +02:00
if ( result [ i ] ) {
2017-05-01 20:47:12 +02:00
// `result[i]` is either a `ResolvedModuleFull` or a marker.
// If it is the former, we can leave it as is.
2017-04-28 05:27:48 +02:00
if ( result [ i ] === predictedToResolveToAmbientModuleMarker ) {
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.
2021-09-24 23:25:59 +02:00
function module NameResolvesToAmbientModuleInNonModifiedFile ( module Name : string , index : number ) : boolean {
if ( index >= length ( oldSourceFile ? . imports ) + length ( oldSourceFile ? . module Augmentations ) ) return false ; // mode index out of bounds, don't reuse resolution
const resolutionToFile = getResolvedModule ( oldSourceFile , module Name , oldSourceFile && getModeForResolutionAtIndex ( oldSourceFile , index ) ) ;
2018-10-21 18:15:01 +02:00
const resolvedFile = resolutionToFile && oldProgram ! . getSourceFile ( resolutionToFile . resolvedFileName ) ;
2019-04-03 23:05:49 +02:00
if ( resolutionToFile && resolvedFile ) {
2017-12-06 22:17:21 +01:00
// 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 ( ) ,
2020-10-19 21:59:59 +02:00
( oldResolvedRef , parent , index ) = > {
2018-10-02 23:10:19 +02:00
const newRef = ( parent ? parent.commandLine.projectReferences : projectReferences ) ! [ index ] ;
const newResolvedRef = parseProjectReferenceConfigFile ( newRef ) ;
if ( oldResolvedRef ) {
// Resolved project reference has gone missing or changed
2021-03-26 21:23:03 +01:00
return ! newResolvedRef ||
newResolvedRef . sourceFile !== oldResolvedRef . sourceFile ||
! arrayIsEqualTo ( oldResolvedRef . commandLine . fileNames , newResolvedRef . commandLine . fileNames ) ;
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 ) ) {
2020-10-10 00:56:51 +02:00
return StructureIsReused . Not ;
2016-04-06 22:49:25 +02:00
}
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 ) ) {
2020-10-10 00:56:51 +02:00
return StructureIsReused . Not ;
2015-06-23 02:48:44 +02:00
}
2015-10-01 01:10:52 +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 ( ) ) {
2020-10-10 00:56:51 +02:00
return StructureIsReused . Not ;
2018-09-14 00:18:53 +02:00
}
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 } [ ] = [ ] ;
2020-10-10 00:56:51 +02:00
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 ) ) ) {
2020-10-10 00:56:51 +02:00
return StructureIsReused . Not ;
2017-07-21 23:58:06 +02:00
}
2017-08-09 23:39:06 +02:00
const oldSourceFiles = oldProgram . getSourceFiles ( ) ;
const enum SeenPackageName { Exists , Modified }
2020-06-26 01:03:25 +02:00
const seenPackageNames = new Map < string , SeenPackageName > ( ) ;
2017-08-09 23:39:06 +02:00
for ( const oldSourceFile of oldSourceFiles ) {
let newSourceFile = host . getSourceFileByPath
2021-09-24 23:25:59 +02:00
? host . getSourceFileByPath ( oldSourceFile . fileName , oldSourceFile . resolvedPath , getEmitScriptTarget ( options ) , /*onError*/ undefined , shouldCreateNewSourceFile )
: host . getSourceFile ( oldSourceFile . fileName , getEmitScriptTarget ( options ) , /*onError*/ undefined , shouldCreateNewSourceFile ) ; // TODO: GH#18217
2016-05-11 08:43:26 +02:00
2015-06-25 02:40:04 +02:00
if ( ! newSourceFile ) {
2020-10-10 00:56:51 +02:00
return 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.
2020-10-10 00:56:51 +02:00
return StructureIsReused . Not ;
2017-08-09 23:39:06 +02:00
}
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 ) {
2020-10-10 00:56:51 +02:00
return StructureIsReused . Not ;
2017-08-09 23:39:06 +02:00
}
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 ;
2021-11-17 22:04:43 +01:00
newSourceFile . impliedNodeFormat = oldSourceFile . impliedNodeFormat ;
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 ) {
2020-10-10 00:56:51 +02:00
return StructureIsReused . Not ;
2017-08-09 23:39:06 +02:00
}
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
2021-05-27 00:57:43 +02:00
structureIsReused = StructureIsReused . SafeModules ;
2018-05-03 20:00:10 +02:00
}
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
2020-10-10 00:56:51 +02:00
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
2020-10-10 00:56:51 +02:00
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
2020-10-10 00:56:51 +02:00
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
2020-10-10 00:56:51 +02:00
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
2020-10-10 00:56:51 +02:00
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
2020-10-10 00:56:51 +02:00
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
2020-10-10 00:56:51 +02:00
structureIsReused = StructureIsReused . SafeModules ;
2017-08-05 14:01:33 +02:00
// 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
2020-10-10 00:56:51 +02:00
if ( structureIsReused !== StructureIsReused . Completely ) {
return 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 ) {
2020-10-31 00:16:23 +01:00
const module Names = getModuleNames ( newSourceFile ) ;
2020-10-06 20:23:05 +02:00
const resolutions = resolveModuleNamesReusingOldState ( module Names , newSourceFile ) ;
2019-08-18 18:48:05 +02:00
// ensure that module resolution results are still correct
2021-09-24 23:25:59 +02:00
const resolutionsChanged = hasChangesInResolutions ( module Names , resolutions , oldSourceFile . resolvedModules , oldSourceFile , module ResolutionIsEqualTo ) ;
2019-08-18 18:48:05 +02:00
if ( resolutionsChanged ) {
2020-10-10 00:56:51 +02:00
structureIsReused = StructureIsReused . SafeModules ;
2021-09-24 23:25:59 +02:00
newSourceFile . resolvedModules = zipToModeAwareCache ( newSourceFile , module Names , resolutions ) ;
2019-08-18 18:48:05 +02:00
}
else {
newSourceFile . resolvedModules = oldSourceFile . resolvedModules ;
2016-11-02 22:41:26 +01:00
}
2020-10-06 20:23:05 +02:00
// We lower-case all type references because npm automatically lowercases all packages. See GH#9824.
const typesReferenceDirectives = map ( newSourceFile . typeReferenceDirectives , ref = > toFileNameLowerCase ( ref . fileName ) ) ;
const typeReferenceResolutions = resolveTypeReferenceDirectiveNamesWorker ( typesReferenceDirectives , newSourceFile ) ;
// ensure that types resolutions are still correct
2021-10-30 00:47:48 +02:00
const typeReferenceResolutionsChanged = hasChangesInResolutions ( typesReferenceDirectives , typeReferenceResolutions , oldSourceFile . resolvedTypeReferenceDirectiveNames , oldSourceFile , typeDirectiveIsEqualTo ) ;
if ( typeReferenceResolutionsChanged ) {
2020-10-10 00:56:51 +02:00
structureIsReused = StructureIsReused . SafeModules ;
2021-09-24 23:25:59 +02:00
newSourceFile . resolvedTypeReferenceDirectiveNames = zipToModeAwareCache ( newSourceFile , typesReferenceDirectives , typeReferenceResolutions ) ;
2020-10-06 20:23:05 +02:00
}
else {
newSourceFile . resolvedTypeReferenceDirectiveNames = oldSourceFile . resolvedTypeReferenceDirectiveNames ;
2016-11-02 22:41:26 +01:00
}
}
2020-10-10 00:56:51 +02:00
if ( structureIsReused !== StructureIsReused . Completely ) {
return structureIsReused ;
2016-11-02 22:41:26 +01:00
}
2021-05-27 00:57:43 +02:00
if ( changesAffectingProgramStructure ( oldOptions , options ) || host . hasChangedAutomaticTypeDirectiveNames ? . ( ) ) {
2020-10-10 00:56:51 +02:00
return StructureIsReused . SafeModules ;
2017-06-22 22:47:54 +02:00
}
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
2019-12-20 16:44:35 +01:00
Debug . assert ( newSourceFiles . length === oldProgram . getSourceFiles ( ) . length ) ;
2018-10-02 22:11:12 +02:00
for ( const newSourceFile of newSourceFiles ) {
2019-12-20 16:44:35 +01:00
filesByName . set ( newSourceFile . path , newSourceFile ) ;
}
const oldFilesByNameMap = oldProgram . getFilesByNameMap ( ) ;
oldFilesByNameMap . forEach ( ( oldFile , path ) = > {
if ( ! oldFile ) {
filesByName . set ( path , oldFile ) ;
return ;
2019-06-26 22:41:46 +02:00
}
2019-12-20 16:44:35 +01:00
if ( oldFile . path === path ) {
// Set the file as found during node modules search if it was found that way in old progra,
if ( oldProgram ! . isSourceFileFromExternalLibrary ( oldFile ) ) {
sourceFilesFoundSearchingNodeModules . set ( oldFile . path , true ) ;
}
return ;
2017-10-24 04:05:38 +02:00
}
2019-12-20 16:44:35 +01:00
filesByName . set ( path , filesByName . get ( oldFile . path ) ) ;
} ) ;
2015-10-01 01:10:52 +02:00
2015-06-23 02:48:44 +02:00
files = newSourceFiles ;
2020-12-09 01:10:05 +01:00
fileReasons = oldProgram . getFileIncludeReasons ( ) ;
2015-09-10 19:46:39 +02:00
fileProcessingDiagnostics = oldProgram . getFileProcessingDiagnostics ( ) ;
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 ;
2021-07-19 18:56:24 +02:00
usesUriStyleNodeCoreModules = oldProgram . usesUriStyleNodeCoreModules ;
2017-08-09 23:39:06 +02:00
2020-10-10 00:56:51 +02:00
return 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 ,
2020-03-20 00:53:44 +01:00
getProjectReferenceRedirect ,
2019-11-27 22:47:19 +01:00
isSourceOfProjectReferenceRedirect ,
2020-07-22 22:53:30 +02:00
getSymlinkCache ,
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-29 20:02:43 +02:00
useCaseSensitiveFileNames : ( ) = > host . useCaseSensitiveFileNames ( ) ,
2019-06-21 22:26:29 +02:00
getProgramBuildInfo : ( ) = > program . getProgramBuildInfo && program . getProgramBuildInfo ( ) ,
getSourceFileFromReference : ( file , ref ) = > program . getSourceFileFromReference ( file , ref ) ,
2019-08-14 01:34:03 +02:00
redirectTargetsMap ,
2021-01-09 00:20:29 +01:00
getFileIncludeReasons : program.getFileIncludeReasons ,
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 {
2020-06-02 20:49:21 +02:00
Debug . assert ( ! outFile ( options ) ) ;
2021-01-13 22:01:53 +01:00
tracing ? . push ( tracing . Phase . Emit , "emitBuildInfo" , { } , /*separateBeginAndEnd*/ true ) ;
2019-02-06 04:35:08 +01:00
performance . mark ( "beforeEmit" ) ;
const emitResult = emitFiles (
notImplementedResolver ,
getEmitHost ( writeFileCallback ) ,
/*targetSourceFile*/ undefined ,
2019-05-08 00:41:39 +02:00
/*transformers*/ noTransformers ,
2019-02-06 04:35:08 +01:00
/*emitOnlyDtsFiles*/ false ,
/*onlyBuildInfo*/ true
) ;
performance . mark ( "afterEmit" ) ;
performance . measure ( "Emit" , "beforeEmit" , "afterEmit" ) ;
2021-01-13 22:01:53 +01:00
tracing ? . pop ( ) ;
2019-02-06 04:35:08 +01:00
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 ,
2020-06-17 20:20:02 +02:00
( _ref , index ) = > resolvedProjectReferences ! [ index ] ? . commandLine ,
2019-02-01 23:46:58 +01:00
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 {
2021-09-15 22:25:08 +02:00
return some ( options . lib , libFileName = > equalityComparer ( file . fileName , pathForLibFile ( 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
}
2019-09-10 01:16:57 +02:00
function emit ( sourceFile? : SourceFile , writeFileCallback? : WriteFileCallback , cancellationToken? : CancellationToken , emitOnlyDtsFiles? : boolean , transformers? : CustomTransformers , forceDtsEmit? : boolean ) : EmitResult {
2021-01-13 22:01:53 +01:00
tracing ? . push ( tracing . Phase . Emit , "emit" , { path : sourceFile?.path } , /*separateBeginAndEnd*/ true ) ;
2020-08-06 20:02:51 +02:00
const result = runWithCancellationToken ( ( ) = > emitWorker ( program , sourceFile , writeFileCallback , cancellationToken , emitOnlyDtsFiles , transformers , forceDtsEmit ) ) ;
2021-01-13 22:01:53 +01:00
tracing ? . pop ( ) ;
2020-08-06 20:02:51 +02:00
return result ;
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
}
2019-09-10 01:16:57 +02:00
function emitWorker ( program : Program , sourceFile : SourceFile | undefined , writeFileCallback : WriteFileCallback | undefined , cancellationToken : CancellationToken | undefined , emitOnlyDtsFiles? : boolean , customTransformers? : CustomTransformers , forceDtsEmit? : boolean ) : EmitResult {
if ( ! forceDtsEmit ) {
2020-06-02 21:38:40 +02:00
const result = handleNoEmitOptions ( program , sourceFile , writeFileCallback , cancellationToken ) ;
2019-12-13 04:51:18 +01:00
if ( result ) return result ;
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.
2020-06-02 20:49:21 +02:00
const emitResolver = getDiagnosticsProducingTypeChecker ( ) . getEmitResolver ( outFile ( options ) ? 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
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 ,
2019-05-08 00:41:39 +02:00
getTransformers ( options , customTransformers , emitOnlyDtsFiles ) ,
2017-02-07 23:36:15 +01:00
emitOnlyDtsFiles ,
2019-09-10 01:16:57 +02:00
/*onlyBuildInfo*/ false ,
forceDtsEmit
2018-05-08 20:20:48 +02:00
) ;
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 > (
2019-12-11 03:25:10 +01:00
sourceFile : SourceFile | undefined ,
getDiagnostics : ( sourceFile : SourceFile , cancellationToken : CancellationToken | undefined ) = > readonly T [ ] ,
cancellationToken : CancellationToken | undefined ) : readonly 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
}
2019-12-11 03:25:10 +01:00
function getSyntacticDiagnostics ( sourceFile? : SourceFile , cancellationToken? : CancellationToken ) : readonly DiagnosticWithLocation [ ] {
2015-06-18 19:52:19 +02:00
return getDiagnosticsHelper ( sourceFile , getSyntacticDiagnosticsForFile , cancellationToken ) ;
2014-12-16 22:14:14 +01:00
}
2019-12-11 03:25:10 +01:00
function getSemanticDiagnostics ( sourceFile? : SourceFile , cancellationToken? : CancellationToken ) : readonly 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
2020-10-10 02:25:04 +02:00
function getCachedSemanticDiagnostics ( sourceFile? : SourceFile ) : readonly Diagnostic [ ] | undefined {
return sourceFile
? cachedBindAndCheckDiagnosticsForFile . perFile ? . get ( sourceFile . path )
: cachedBindAndCheckDiagnosticsForFile . allDiagnostics ;
}
2019-12-11 03:25:10 +01:00
function getBindAndCheckDiagnostics ( sourceFile : SourceFile , cancellationToken? : CancellationToken ) : readonly Diagnostic [ ] {
return getBindAndCheckDiagnosticsForFile ( sourceFile , cancellationToken ) ;
}
function getProgramDiagnostics ( sourceFile : SourceFile ) : readonly Diagnostic [ ] {
if ( skipTypeChecking ( sourceFile , options , program ) ) {
return emptyArray ;
}
const programDiagnosticsInFile = programDiagnostics . getDiagnostics ( sourceFile . fileName ) ;
2020-03-05 16:37:36 +01:00
if ( ! sourceFile . commentDirectives ? . length ) {
2020-12-09 01:10:05 +01:00
return programDiagnosticsInFile ;
2019-12-11 03:25:10 +01:00
}
2020-03-05 16:37:36 +01:00
2020-12-09 01:10:05 +01:00
return getDiagnosticsWithPrecedingDirectives ( sourceFile , sourceFile . commentDirectives , programDiagnosticsInFile ) . diagnostics ;
2019-12-11 03:25:10 +01:00
}
function getDeclarationDiagnostics ( sourceFile? : SourceFile , cancellationToken? : CancellationToken ) : readonly 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)
2020-06-02 20:49:21 +02:00
if ( ! sourceFile || outFile ( options ) ) {
2016-02-24 23:21:30 +01:00
return getDeclarationDiagnosticsWorker ( sourceFile , cancellationToken ) ;
}
else {
return getDiagnosticsHelper ( sourceFile , getDeclarationDiagnosticsForFile , cancellationToken ) ;
}
2015-03-20 00:55:07 +01:00
}
2019-08-08 20:30:18 +02:00
function getSyntacticDiagnosticsForFile ( sourceFile : SourceFile ) : readonly 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
2019-12-11 03:25:10 +01:00
function getSemanticDiagnosticsForFile ( sourceFile : SourceFile , cancellationToken : CancellationToken | undefined ) : readonly Diagnostic [ ] {
return concatenate (
Issue "Cannot find name did-you-mean" errors as suggestions in plain JS (#44271)
* Always issue cannot find name did-you-mean error
This PR issues "cannot find ${name}, did you mean ${name}" errors for
identifiers and propery access expressions in JS files *without*
`// @ts-check` and without `// @ts-nocheck`. This brings some benefits of
Typescript's binder to all Javascript users, even those who haven't
opted into Typescript checking.
```js
export var inModule = 1
inmodule.toFixed() // errors on exports
function f() {
var locals = 2
locale.toFixed() // errors on locals
}
var object = {
spaaace: 3
}
object.spaaaace // error on read
object.spaace = 2 // error on write
object.fresh = 12 // OK, no spelling correction to offer
```
To disable the errors, add `// @ts-nocheck` to the file. To get the
normal checkJs experience, add `// @ts-check`.
== Why This Works ==
In a word: precision. This change has low recall — it misses lots
of correct errors that would be nice to show — but it has high
precision: almost all the errors it shows are correct. And they come
with a suggested correction.
Here are the ingredients:
1. For unchecked JS files, the compiler suppresses all errors except
two did-you-mean name resolution errors.
2. Did-you-mean spelling correction is already tuned for high
precision/low recall, and doesn't show many bogus errors even in JS.
3. For identifiers, the error is suppressed for suggestions from global files.
These are often DOM feature detection, for example.
4. For property accesses, the error is suppressed for suggestions from
other files, for the same reason.
5. For property accesses, the error is suppressed for `this` property
accesses because the compiler doesn't understand JS constructor
functions well enough.
In particular, it doesn't understand any inheritance patterns.
== Work Remaining ==
1. Code cleanup.
2. Fix a couple of failures in existing tests.
3. Suppress errors on property access suggestions from large objects.
4. Combine (3) and (4) above to suppress errors on suggestions from other, global files.
5. A little more testing on random files to make sure that precision
is good there too.
6. Have people from the regular Code editor meeting test the code and
suggest ideas.
* all (most?) tests pass
* NOW they all pass
* add tonnes of semi-colons
* restore this.x check+add a test case
* make ts-ignore/no-check codefix work in unchecked js
* Issues errors only in the language service
* add a few more tests
* fix incorrect parentheses
* More cleanup in program.ts
* Improve readability of isExcludedJSError
* make diff in program.ts smaller via closure
* Switch unchecked JS did-you-mean to suggestion
Instead of selectively letting errors through.
* undo more missed changes
* disallow ignoring suggestions
* Issue different messages for plain JS than others
Straw text for the messages, I just changed the modals to avoid name
collisions.
2021-06-15 17:54:08 +02:00
filterSemanticDiagnostics ( getBindAndCheckDiagnosticsForFile ( sourceFile , cancellationToken ) , options ) ,
2019-12-11 03:25:10 +01:00
getProgramDiagnostics ( sourceFile )
) ;
2017-03-07 22:26:41 +01:00
}
2019-12-11 03:25:10 +01:00
function getBindAndCheckDiagnosticsForFile ( sourceFile : SourceFile , cancellationToken : CancellationToken | undefined ) : readonly Diagnostic [ ] {
return getAndCacheDiagnostics ( sourceFile , cancellationToken , cachedBindAndCheckDiagnosticsForFile , getBindAndCheckDiagnosticsForFileNoCache ) ;
}
function getBindAndCheckDiagnosticsForFileNoCache ( sourceFile : SourceFile , cancellationToken : CancellationToken | undefined ) : readonly Diagnostic [ ] {
2015-06-18 19:52:19 +02:00
return runWithCancellationToken ( ( ) = > {
2019-07-01 23:43:11 +02:00
if ( skipTypeChecking ( sourceFile , options , program ) ) {
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 ) ;
2019-09-27 21:06:05 +02:00
const isTsNoCheck = ! ! sourceFile . checkJsDirective && sourceFile . checkJsDirective . enabled === false ;
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)
Issue "Cannot find name did-you-mean" errors as suggestions in plain JS (#44271)
* Always issue cannot find name did-you-mean error
This PR issues "cannot find ${name}, did you mean ${name}" errors for
identifiers and propery access expressions in JS files *without*
`// @ts-check` and without `// @ts-nocheck`. This brings some benefits of
Typescript's binder to all Javascript users, even those who haven't
opted into Typescript checking.
```js
export var inModule = 1
inmodule.toFixed() // errors on exports
function f() {
var locals = 2
locale.toFixed() // errors on locals
}
var object = {
spaaace: 3
}
object.spaaaace // error on read
object.spaace = 2 // error on write
object.fresh = 12 // OK, no spelling correction to offer
```
To disable the errors, add `// @ts-nocheck` to the file. To get the
normal checkJs experience, add `// @ts-check`.
== Why This Works ==
In a word: precision. This change has low recall — it misses lots
of correct errors that would be nice to show — but it has high
precision: almost all the errors it shows are correct. And they come
with a suggested correction.
Here are the ingredients:
1. For unchecked JS files, the compiler suppresses all errors except
two did-you-mean name resolution errors.
2. Did-you-mean spelling correction is already tuned for high
precision/low recall, and doesn't show many bogus errors even in JS.
3. For identifiers, the error is suppressed for suggestions from global files.
These are often DOM feature detection, for example.
4. For property accesses, the error is suppressed for suggestions from
other files, for the same reason.
5. For property accesses, the error is suppressed for `this` property
accesses because the compiler doesn't understand JS constructor
functions well enough.
In particular, it doesn't understand any inheritance patterns.
== Work Remaining ==
1. Code cleanup.
2. Fix a couple of failures in existing tests.
3. Suppress errors on property access suggestions from large objects.
4. Combine (3) and (4) above to suppress errors on suggestions from other, global files.
5. A little more testing on random files to make sure that precision
is good there too.
6. Have people from the regular Code editor meeting test the code and
suggest ideas.
* all (most?) tests pass
* NOW they all pass
* add tonnes of semi-colons
* restore this.x check+add a test case
* make ts-ignore/no-check codefix work in unchecked js
* Issues errors only in the language service
* add a few more tests
* fix incorrect parentheses
* More cleanup in program.ts
* Improve readability of isExcludedJSError
* make diff in program.ts smaller via closure
* Switch unchecked JS did-you-mean to suggestion
Instead of selectively letting errors through.
* undo more missed changes
* disallow ignoring suggestions
* Issue different messages for plain JS than others
Straw text for the messages, I just changed the modals to avoid name
collisions.
2021-06-15 17:54:08 +02:00
const includeBindAndCheckDiagnostics = ! isTsNoCheck && ( sourceFile . scriptKind === ScriptKind . TS || sourceFile . scriptKind === ScriptKind . TSX
|| sourceFile . scriptKind === ScriptKind . External || isCheckJs || sourceFile . scriptKind === ScriptKind . Deferred ) ;
2019-08-08 20:30:18 +02:00
const bindDiagnostics : readonly Diagnostic [ ] = includeBindAndCheckDiagnostics ? sourceFile.bindDiagnostics : emptyArray ;
2017-06-02 04:05:07 +02:00
const checkDiagnostics = includeBindAndCheckDiagnostics ? typeChecker . getDiagnostics ( sourceFile , cancellationToken ) : emptyArray ;
2018-05-22 20:01:18 +02:00
2020-08-25 20:04:23 +02:00
return getMergedBindAndCheckDiagnostics ( sourceFile , includeBindAndCheckDiagnostics , bindDiagnostics , checkDiagnostics , isCheckJs ? sourceFile.jsDocDiagnostics : undefined ) ;
2015-06-18 19:52:19 +02:00
} ) ;
2014-12-16 22:14:14 +01:00
}
2020-08-25 20:04:23 +02:00
function getMergedBindAndCheckDiagnostics ( sourceFile : SourceFile , includeBindAndCheckDiagnostics : boolean , . . . allDiagnostics : ( readonly Diagnostic [ ] | undefined ) [ ] ) {
2020-03-05 16:37:36 +01:00
const flatDiagnostics = flatten ( allDiagnostics ) ;
2020-08-25 20:04:23 +02:00
if ( ! includeBindAndCheckDiagnostics || ! sourceFile . commentDirectives ? . length ) {
2020-03-05 16:37:36 +01:00
return flatDiagnostics ;
}
const { diagnostics , directives } = getDiagnosticsWithPrecedingDirectives ( sourceFile , sourceFile . commentDirectives , flatDiagnostics ) ;
for ( const errorExpectation of directives . getUnusedExpectations ( ) ) {
diagnostics . push ( createDiagnosticForRange ( sourceFile , errorExpectation . range , Diagnostics . Unused_ts_expect_error_directive ) ) ;
}
return diagnostics ;
}
/ * *
* Creates a map of comment directives along with the diagnostics immediately preceded by one of them .
* Comments that match to any of those diagnostics are marked as used .
* /
function getDiagnosticsWithPrecedingDirectives ( sourceFile : SourceFile , commentDirectives : CommentDirective [ ] , flatDiagnostics : Diagnostic [ ] ) {
// Diagnostics are only reported if there is no comment directive preceding them
// This will modify the directives map by marking "used" ones with a corresponding diagnostic
const directives = createCommentDirectivesMap ( sourceFile , commentDirectives ) ;
const diagnostics = flatDiagnostics . filter ( diagnostic = > markPrecedingCommentDirectiveLine ( diagnostic , directives ) === - 1 ) ;
return { diagnostics , directives } ;
}
2019-08-08 20:30:18 +02:00
function getSuggestionDiagnostics ( sourceFile : SourceFile , cancellationToken : CancellationToken ) : readonly DiagnosticWithLocation [ ] {
2018-05-31 19:47:17 +02:00
return runWithCancellationToken ( ( ) = > {
return getDiagnosticsProducingTypeChecker ( ) . getSuggestionDiagnostics ( sourceFile , cancellationToken ) ;
} ) ;
}
2017-03-08 08:03:47 +01:00
/ * *
2020-03-05 16:37:36 +01:00
* @returns The line index marked as preceding the diagnostic , or - 1 if none was .
2017-03-08 08:03:47 +01:00
* /
2020-03-05 16:37:36 +01:00
function markPrecedingCommentDirectiveLine ( diagnostic : Diagnostic , directives : CommentDirectivesMap ) {
2017-03-08 08:03:47 +01:00
const { file , start } = diagnostic ;
2020-03-05 16:37:36 +01:00
if ( ! file ) {
return - 1 ;
}
// Start out with the line just before the text
const lineStarts = getLineStarts ( file ) ;
let line = computeLineAndCharacterOfPosition ( lineStarts , start ! ) . line - 1 ; // TODO: GH#18217
while ( line >= 0 ) {
// As soon as that line is known to have a comment directive, use that
if ( directives . markUsed ( line ) ) {
return line ;
2017-03-08 08:03:47 +01:00
}
2020-03-05 16:37:36 +01:00
// Stop searching if the line is not empty and not a comment
2020-05-04 22:02:54 +02:00
const lineText = file . text . slice ( lineStarts [ line ] , lineStarts [ line + 1 ] ) . trim ( ) ;
2020-03-05 16:37:36 +01:00
if ( lineText !== "" && ! /^(\s*)\/\/(.*)$/ . test ( lineText ) ) {
return - 1 ;
}
line -- ;
2017-03-08 08:03:47 +01:00
}
2020-03-05 16:37:36 +01:00
return - 1 ;
2017-03-08 08:03:47 +01:00
}
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 [ ] = [ ] ;
2020-02-25 22:29:34 +01:00
walk ( sourceFile , sourceFile ) ;
forEachChildRecursively ( sourceFile , walk , walkArray ) ;
2015-09-09 23:41:19 +02:00
return diagnostics ;
2020-02-25 22:29:34 +01:00
function walk ( node : Node , parent : Node ) {
2016-11-01 20:54:27 +01:00
// 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 :
2019-08-18 21:46:53 +02:00
case SyntaxKind . MethodDeclaration :
2021-05-18 15:20:57 +02:00
if ( ( parent as ParameterDeclaration | PropertyDeclaration | MethodDeclaration ) . questionToken === node ) {
2019-11-22 23:51:22 +01:00
diagnostics . push ( createDiagnosticForNode ( node , Diagnostics . The_0_modifier_can_only_be_used_in_TypeScript_files , "?" ) ) ;
2020-02-25 22:29:34 +01:00
return "skip" ;
2016-11-01 20:54:27 +01:00
}
2017-08-15 03:19:43 +02:00
// falls through
2016-11-01 20:54:27 +01:00
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
2021-05-18 15:20:57 +02:00
if ( ( parent as FunctionLikeDeclaration | VariableDeclaration | ParameterDeclaration | PropertyDeclaration ) . type === node ) {
2019-11-22 23:51:22 +01:00
diagnostics . push ( createDiagnosticForNode ( node , Diagnostics . Type_annotations_can_only_be_used_in_TypeScript_files ) ) ;
2020-02-25 22:29:34 +01:00
return "skip" ;
2016-11-01 20:54:27 +01:00
}
2015-09-09 23:41:19 +02:00
}
switch ( node . kind ) {
2020-01-03 23:39:32 +01:00
case SyntaxKind . ImportClause :
if ( ( node as ImportClause ) . isTypeOnly ) {
2020-08-18 00:30:19 +02:00
diagnostics . push ( createDiagnosticForNode ( parent , Diagnostics . _0_declarations_can_only_be_used_in_TypeScript_files , "import type" ) ) ;
2020-02-25 22:29:34 +01:00
return "skip" ;
2020-01-03 23:39:32 +01:00
}
break ;
case SyntaxKind . ExportDeclaration :
if ( ( node as ExportDeclaration ) . isTypeOnly ) {
diagnostics . push ( createDiagnosticForNode ( node , Diagnostics . _0_declarations_can_only_be_used_in_TypeScript_files , "export type" ) ) ;
2020-02-25 22:29:34 +01:00
return "skip" ;
2020-01-03 23:39:32 +01:00
}
break ;
2015-09-09 23:41:19 +02:00
case SyntaxKind . ImportEqualsDeclaration :
2019-11-22 23:51:22 +01:00
diagnostics . push ( createDiagnosticForNode ( node , Diagnostics . import_can_only_be_used_in_TypeScript_files ) ) ;
2020-02-25 22:29:34 +01:00
return "skip" ;
2015-09-09 23:41:19 +02:00
case SyntaxKind . ExportAssignment :
2021-05-18 15:20:57 +02:00
if ( ( node as ExportAssignment ) . isExportEquals ) {
2019-11-22 23:51:22 +01:00
diagnostics . push ( createDiagnosticForNode ( node , Diagnostics . export_can_only_be_used_in_TypeScript_files ) ) ;
2020-02-25 22:29:34 +01:00
return "skip" ;
2015-09-09 23:41:19 +02:00
}
break ;
case SyntaxKind . HeritageClause :
2021-05-18 15:20:57 +02:00
const heritageClause = node as HeritageClause ;
2015-09-09 23:41:19 +02:00
if ( heritageClause . token === SyntaxKind . ImplementsKeyword ) {
2019-11-22 23:51:22 +01:00
diagnostics . push ( createDiagnosticForNode ( node , Diagnostics . implements_clauses_can_only_be_used_in_TypeScript_files ) ) ;
2020-02-25 22:29:34 +01:00
return "skip" ;
2015-09-09 23:41:19 +02:00
}
break ;
case SyntaxKind . InterfaceDeclaration :
2020-01-03 01:30:46 +01:00
const interfaceKeyword = tokenToString ( SyntaxKind . InterfaceKeyword ) ;
2020-02-25 03:20:58 +01:00
Debug . assertIsDefined ( interfaceKeyword ) ;
2020-01-03 01:30:46 +01:00
diagnostics . push ( createDiagnosticForNode ( node , Diagnostics . _0_declarations_can_only_be_used_in_TypeScript_files , interfaceKeyword ) ) ;
2020-02-25 22:29:34 +01:00
return "skip" ;
2015-09-09 23:41:19 +02:00
case SyntaxKind . ModuleDeclaration :
2019-11-22 23:51:22 +01:00
const module Keyword = node . flags & NodeFlags . Namespace ? tokenToString ( SyntaxKind . NamespaceKeyword ) : tokenToString ( SyntaxKind . ModuleKeyword ) ;
2020-02-25 03:20:58 +01:00
Debug . assertIsDefined ( module Keyword ) ;
2019-11-22 23:51:22 +01:00
diagnostics . push ( createDiagnosticForNode ( node , Diagnostics . _0_declarations_can_only_be_used_in_TypeScript_files , module Keyword ) ) ;
2020-02-25 22:29:34 +01:00
return "skip" ;
2015-09-09 23:41:19 +02:00
case SyntaxKind . TypeAliasDeclaration :
2019-11-22 23:51:22 +01:00
diagnostics . push ( createDiagnosticForNode ( node , Diagnostics . Type_aliases_can_only_be_used_in_TypeScript_files ) ) ;
2020-02-25 22:29:34 +01:00
return "skip" ;
2016-11-01 20:54:27 +01:00
case SyntaxKind . EnumDeclaration :
2020-02-25 03:20:58 +01:00
const enumKeyword = Debug . checkDefined ( tokenToString ( SyntaxKind . EnumKeyword ) ) ;
2019-11-22 23:51:22 +01:00
diagnostics . push ( createDiagnosticForNode ( node , Diagnostics . _0_declarations_can_only_be_used_in_TypeScript_files , enumKeyword ) ) ;
2020-02-25 22:29:34 +01:00
return "skip" ;
2017-07-29 14:41:08 +02:00
case SyntaxKind . NonNullExpression :
2019-11-22 23:51:22 +01:00
diagnostics . push ( createDiagnosticForNode ( node , Diagnostics . Non_null_assertions_can_only_be_used_in_TypeScript_files ) ) ;
2020-02-25 22:29:34 +01:00
return "skip" ;
2017-07-29 14:41:08 +02:00
case SyntaxKind . AsExpression :
2019-11-22 23:51:22 +01:00
diagnostics . push ( createDiagnosticForNode ( ( node as AsExpression ) . type , Diagnostics . Type_assertion_expressions_can_only_be_used_in_TypeScript_files ) ) ;
2020-02-25 22:29:34 +01:00
return "skip" ;
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
}
}
2020-02-25 22:29:34 +01:00
function walkArray ( nodes : NodeArray < Node > , parent : Node ) {
2016-11-01 20:54:27 +01:00
if ( parent . decorators === nodes && ! options . experimentalDecorators ) {
2019-04-25 23:55:04 +02:00
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_in_your_tsconfig_or_jsconfig_to_remove_this_warning ) ) ;
2016-11-01 20:54:27 +01:00
}
switch ( parent . kind ) {
case SyntaxKind . ClassDeclaration :
2019-08-16 00:49:50 +02:00
case SyntaxKind . ClassExpression :
2015-09-09 23:41:19 +02:00
case SyntaxKind . MethodDeclaration :
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
2021-05-18 15:20:57 +02:00
if ( nodes === ( parent as DeclarationWithTypeParameterChildren ) . typeParameters ) {
2019-11-22 23:51:22 +01:00
diagnostics . push ( createDiagnosticForNodeArray ( nodes , Diagnostics . Type_parameter_declarations_can_only_be_used_in_TypeScript_files ) ) ;
2020-02-25 22:29:34 +01:00
return "skip" ;
2015-09-09 23:41:19 +02:00
}
2020-03-17 22:21:23 +01:00
// falls through
2019-06-21 17:32:22 +02:00
2015-09-09 23:41:19 +02:00
case SyntaxKind . VariableStatement :
2016-11-01 20:54:27 +01:00
// Check modifiers
2019-08-18 21:46:53 +02:00
if ( nodes === parent . modifiers ) {
2020-02-25 22:29:34 +01:00
checkModifiers ( parent . modifiers , parent . kind === SyntaxKind . VariableStatement ) ;
return "skip" ;
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
2021-05-18 15:20:57 +02:00
if ( nodes === ( parent as PropertyDeclaration ) . modifiers ) {
for ( const modifier of nodes as NodeArray < Modifier > ) {
2016-06-15 18:42:52 +02:00
if ( modifier . kind !== SyntaxKind . StaticKeyword ) {
2019-11-22 23:51:22 +01:00
diagnostics . push ( createDiagnosticForNode ( modifier , Diagnostics . The_0_modifier_can_only_be_used_in_TypeScript_files , tokenToString ( modifier . kind ) ) ) ;
2016-06-15 18:42:52 +02:00
}
}
2020-02-25 22:29:34 +01:00
return "skip" ;
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
2021-05-18 15:20:57 +02:00
if ( nodes === ( parent as ParameterDeclaration ) . modifiers ) {
2019-11-22 23:51:22 +01:00
diagnostics . push ( createDiagnosticForNodeArray ( nodes , Diagnostics . Parameter_modifiers_can_only_be_used_in_TypeScript_files ) ) ;
2020-02-25 22:29:34 +01:00
return "skip" ;
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 :
2019-08-18 21:46:53 +02:00
case SyntaxKind . TaggedTemplateExpression :
2016-11-01 20:54:27 +01:00
// Check type arguments
2021-05-18 15:20:57 +02:00
if ( nodes === ( parent as NodeWithTypeArguments ) . typeArguments ) {
2019-11-22 23:51:22 +01:00
diagnostics . push ( createDiagnosticForNodeArray ( nodes , Diagnostics . Type_arguments_can_only_be_used_in_TypeScript_files ) ) ;
2020-02-25 22:29:34 +01:00
return "skip" ;
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-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 :
2021-03-27 00:29:22 +01:00
case SyntaxKind . OverrideKeyword :
2019-11-22 23:51:22 +01:00
diagnostics . push ( createDiagnosticForNode ( modifier , Diagnostics . The_0_modifier_can_only_be_used_in_TypeScript_files , tokenToString ( modifier . kind ) ) ) ;
2016-11-01 20:54:27 +01:00
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
}
} ) ;
}
2019-12-11 03:25:10 +01:00
function getDeclarationDiagnosticsWorker ( sourceFile : SourceFile | undefined , cancellationToken : CancellationToken | undefined ) : readonly DiagnosticWithLocation [ ] {
2017-03-07 22:26:41 +01:00
return getAndCacheDiagnostics ( sourceFile , cancellationToken , cachedDeclarationDiagnosticsForFile , getDeclarationDiagnosticsForFileNoCache ) ;
}
2019-12-11 03:25:10 +01:00
function getDeclarationDiagnosticsForFileNoCache ( sourceFile : SourceFile | undefined , cancellationToken : CancellationToken | undefined ) : readonly DiagnosticWithLocation [ ] {
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.
2019-12-11 03:25:10 +01:00
return ts . getDeclarationDiagnostics ( getEmitHost ( noop ) , resolver , sourceFile ) || emptyArray ;
2015-06-18 19:52:19 +02:00
} ) ;
2015-03-20 00:55:07 +01:00
}
2019-12-11 03:25:10 +01:00
function getAndCacheDiagnostics < T extends SourceFile | undefined , U extends Diagnostic > (
sourceFile : T ,
cancellationToken : CancellationToken | undefined ,
cache : DiagnosticCache < U > ,
getDiagnostics : ( sourceFile : T , cancellationToken : CancellationToken | undefined ) = > readonly U [ ] ,
) : readonly U [ ] {
2017-03-07 22:26:41 +01:00
const cachedResult = sourceFile
2020-06-26 19:12:47 +02:00
? cache . perFile ? . get ( sourceFile . path )
2019-12-11 03:25:10 +01:00
: cache . allDiagnostics ;
2017-03-07 22:26:41 +01:00
if ( cachedResult ) {
return cachedResult ;
}
2019-12-11 03:25:10 +01:00
const result = getDiagnostics ( sourceFile , cancellationToken ) ;
2017-03-07 22:26:41 +01:00
if ( sourceFile ) {
2020-06-26 19:12:47 +02:00
( cache . perFile || ( cache . perFile = new Map ( ) ) ) . set ( sourceFile . path , result ) ;
2017-03-07 22:26:41 +01:00
}
else {
cache . allDiagnostics = result ;
}
return result ;
}
2019-08-08 20:30:18 +02:00
function getDeclarationDiagnosticsForFile ( sourceFile : SourceFile , cancellationToken : CancellationToken ) : readonly 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 (
2020-12-09 01:10:05 +01:00
programDiagnostics . getGlobalDiagnostics ( ) ,
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() {
2021-08-16 22:53:51 +02:00
if ( ! options . configFile ) return emptyArray ;
2018-10-04 01:09:16 +02:00
let diagnostics = programDiagnostics . getDiagnostics ( options . configFile . fileName ) ;
forEachResolvedProjectReference ( resolvedRef = > {
2020-10-19 21:59:59 +02:00
diagnostics = concatenate ( diagnostics , programDiagnostics . getDiagnostics ( resolvedRef . sourceFile . fileName ) ) ;
2018-10-04 01:09:16 +02:00
} ) ;
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
}
2019-08-08 20:30:18 +02:00
function getConfigFileParsingDiagnostics ( ) : readonly Diagnostic [ ] {
2018-03-15 22:32:01 +01:00
return configFileParsingDiagnostics || emptyArray ;
}
2020-12-09 01:10:05 +01:00
function processRootFile ( fileName : string , isDefaultLib : boolean , ignoreNoDefaultLib : boolean , reason : FileIncludeReason ) {
processSourceFile ( normalizePath ( fileName ) , isDefaultLib , ignoreNoDefaultLib , /*packageId*/ undefined , reason ) ;
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
}
2020-10-31 00:16:23 +01:00
function createSyntheticImport ( text : string , file : SourceFile ) {
const externalHelpersModuleReference = factory . createStringLiteral ( text ) ;
2021-09-20 23:15:22 +02:00
const importDecl = factory . createImportDeclaration ( /*decorators*/ undefined , /*modifiers*/ undefined , /*importClause*/ undefined , externalHelpersModuleReference , /*assertClause*/ undefined ) ;
2020-10-31 00:16:23 +01:00
addEmitFlags ( importDecl , EmitFlags . NeverApplyImportHelper ) ;
setParent ( externalHelpersModuleReference , importDecl ) ;
setParent ( importDecl , file ) ;
2020-11-02 01:05:49 +01:00
// explicitly unset the synthesized flag on these declarations so the checker API will answer questions about them
// (which is required to build the dependency graph for incremental emit)
( externalHelpersModuleReference as Mutable < Node > ) . flags &= ~ NodeFlags . Synthesized ;
( importDecl as Mutable < Node > ) . flags &= ~ NodeFlags . Synthesized ;
2020-10-31 00:16:23 +01:00
return externalHelpersModuleReference ;
}
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.
2020-10-31 00:16:23 +01:00
if ( ( options . isolatedModules || isExternalModuleFile )
2016-06-15 01:24:01 +02:00
&& ! file . isDeclarationFile ) {
2020-10-31 00:16:23 +01:00
if ( options . importHelpers ) {
// synthesize 'import "tslib"' declaration
imports = [ createSyntheticImport ( externalHelpersModuleNameText , file ) ] ;
}
const jsxImport = getJSXRuntimeImport ( getJSXImplicitImportBase ( options , file ) , options ) ;
if ( jsxImport ) {
// synthesize `import "base/jsx-runtime"` declaration
2020-10-31 00:21:36 +01:00
( imports || = [ ] ) . push ( createSyntheticImport ( jsxImport , file ) ) ;
2020-10-31 00:16:23 +01:00
}
2016-06-15 01:24:01 +02:00
}
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 ) ) ) {
2021-09-24 23:25:59 +02:00
setParentRecursive ( node , /*incremental*/ false ) ; // we need parent data on imports before the program is fully bound, so we ensure it's set here
2018-03-16 22:01:00 +01:00
imports = append ( imports , module NameExpr ) ;
2021-07-19 18:56:24 +02:00
if ( ! usesUriStyleNodeCoreModules && currentNodeModulesDepth === 0 && ! file . isDeclarationFile ) {
usesUriStyleNodeCoreModules = startsWith ( module NameExpr.text , "node:" ) ;
}
2018-03-16 22:01:00 +01:00
}
}
else if ( isModuleDeclaration ( node ) ) {
2020-05-12 00:07:43 +02:00
if ( isAmbientModule ( node ) && ( inAmbientModule || hasSyntacticModifier ( node , ModifierFlags . Ambient ) || file . isDeclarationFile ) ) {
2021-09-24 23:25:59 +02:00
( node . name as Mutable < Node > ) . parent = node ;
2018-03-16 22:01:00 +01:00
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
2021-05-18 15:20:57 +02:00
const body = ( node as ModuleDeclaration ) . body as ModuleBlock ;
2018-03-16 22:01:00 +01:00
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 ;
2019-07-18 09:50:38 +02:00
while ( r . exec ( file . text ) !== null ) { // eslint-disable-line no-null/no-null
2018-10-26 18:50:12 +02:00
const node = getNodeAtPosition ( file , r . lastIndex ) ;
2020-07-15 23:31:55 +02:00
if ( isJavaScriptFile && isRequireCall ( node , /*checkArgumentIsStringLiteralLike*/ true ) ) {
2021-09-24 23:25:59 +02:00
setParentRecursive ( node , /*incremental*/ false ) ; // we need parent data on imports before the program is fully bound, so we ensure it's set here
2018-10-24 20:27:39 +02:00
imports = append ( imports , node . arguments [ 0 ] ) ;
}
2021-09-20 23:15:22 +02:00
// we have to check the argument list has length of at least 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 ] ) ) {
2021-09-24 23:25:59 +02:00
setParentRecursive ( node , /*incremental*/ false ) ; // we need parent data on imports before the program is fully bound, so we ensure it's set here
2021-07-01 21:21:27 +02:00
imports = append ( imports , node . arguments [ 0 ] ) ;
2018-10-24 20:27:39 +02:00
}
else if ( isLiteralImportTypeNode ( node ) ) {
2021-09-24 23:25:59 +02:00
setParentRecursive ( node , /*incremental*/ false ) ; // we need parent data on imports before the program is fully bound, so we ensure it's set here
2018-10-24 20:27:39 +02:00
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 ) {
2020-01-31 19:40:57 +01:00
const libName = toFileNameLowerCase ( ref . fileName ) ;
2018-05-31 18:50:51 +02:00
const libFileName = libMap . get ( libName ) ;
if ( libFileName ) {
2021-09-15 22:25:08 +02:00
return getSourceFile ( pathForLibFile ( libFileName ) ) ;
2018-05-31 18:50:51 +02:00
}
}
2017-05-09 21:20:52 +02:00
/** This should have similar behavior to 'processSourceFile' without diagnostics or mutation. */
2019-06-21 22:26:29 +02:00
function getSourceFileFromReference ( referencingFile : SourceFile | UnparsedSource , ref : FileReference ) : SourceFile | undefined {
2020-12-09 01:10:05 +01:00
return getSourceFileFromReferenceWorker ( resolveTripleslashReference ( ref . fileName , referencingFile . fileName ) , getSourceFile ) ;
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 ,
2020-12-09 01:10:05 +01:00
reason? : FileIncludeReason ) : SourceFile | undefined {
2017-05-09 21:20:52 +02:00
2015-02-04 01:08:46 +01:00
if ( hasExtension ( fileName ) ) {
2019-11-04 20:59:06 +01:00
const canonicalFileName = host . getCanonicalFileName ( fileName ) ;
2021-09-24 23:25:59 +02:00
if ( ! options . allowNonTsExtensions && ! forEach ( flatten ( supportedExtensionsWithJsonIfResolveJsonModule ) , extension = > fileExtensionIs ( canonicalFileName , extension ) ) ) {
2019-11-04 20:59:06 +01:00
if ( fail ) {
if ( hasJSFileExtension ( canonicalFileName ) ) {
fail ( Diagnostics . File_0_is_a_JavaScript_file_Did_you_mean_to_enable_the_allowJs_option , fileName ) ;
}
else {
2021-09-24 23:25:59 +02:00
fail ( Diagnostics . File_0_has_an_unsupported_extension_The_only_supported_extensions_are_1 , fileName , "'" + flatten ( supportedExtensions ) . join ( "', '" ) + "'" ) ;
2019-11-04 20:59:06 +01:00
}
}
2017-05-09 21:20:52 +02:00
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
}
2020-12-09 01:10:05 +01:00
else if ( isReferencedFile ( reason ) && canonicalFileName === host . getCanonicalFileName ( getSourceFileByPath ( reason . file ) ! . 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
2021-09-24 23:25:59 +02:00
// Only try adding extensions from the first supported group (which should be .ts/.tsx/.d.ts)
const sourceFileWithAddedExtension = forEach ( supportedExtensions [ 0 ] , extension = > getSourceFile ( fileName + extension ) ) ;
if ( fail && ! sourceFileWithAddedExtension ) fail ( Diagnostics . Could_not_resolve_the_path_0_with_the_extensions_Colon_1 , fileName , "'" + flatten ( supportedExtensions ) . join ( "', '" ) + "'" ) ;
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`. */
2020-12-09 01:10:05 +01:00
function processSourceFile ( fileName : string , isDefaultLib : boolean , ignoreNoDefaultLib : boolean , packageId : PackageId | undefined , reason : FileIncludeReason ) : void {
2019-08-09 20:15:20 +02:00
getSourceFileFromReferenceWorker (
fileName ,
2021-08-27 00:35:04 +02:00
fileName = > findSourceFile ( fileName , isDefaultLib , ignoreNoDefaultLib , reason , packageId ) , // TODO: GH#18217
2020-12-09 01:10:05 +01:00
( diagnostic , . . . args ) = > addFilePreprocessingFileExplainingDiagnostic ( /*file*/ undefined , reason , diagnostic , args ) ,
reason
2019-08-09 20:15:20 +02:00
) ;
2017-05-09 21:20:52 +02:00
}
2020-12-09 01:10:05 +01:00
function processProjectReferenceFile ( fileName : string , reason : ProjectReferenceFile ) {
return processSourceFile ( fileName , /*isDefaultLib*/ false , /*ignoreNoDefaultLib*/ false , /*packageId*/ undefined , reason ) ;
}
function reportFileNamesDifferOnlyInCasingError ( fileName : string , existingFile : SourceFile , reason : FileIncludeReason ) : void {
const hasExistingReasonToReportErrorOn = ! isReferencedFile ( reason ) && some ( fileReasons . get ( existingFile . path ) , isReferencedFile ) ;
if ( hasExistingReasonToReportErrorOn ) {
addFilePreprocessingFileExplainingDiagnostic ( existingFile , reason , Diagnostics . Already_included_file_name_0_differs_from_file_name_1_only_in_casing , [ existingFile . fileName , fileName ] ) ;
}
else {
addFilePreprocessingFileExplainingDiagnostic ( existingFile , reason , Diagnostics . File_name_0_differs_from_already_included_file_name_1_only_in_casing , [ fileName , existingFile . fileName ] ) ;
}
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
2021-08-27 00:35:04 +02:00
function findSourceFile ( fileName : string , isDefaultLib : boolean , ignoreNoDefaultLib : boolean , reason : FileIncludeReason , packageId : PackageId | undefined ) : SourceFile | undefined {
2021-01-13 22:01:53 +01:00
tracing ? . push ( tracing . Phase . Program , "findSourceFile" , {
2020-10-09 02:08:21 +02:00
fileName ,
isDefaultLib : isDefaultLib || undefined ,
2020-12-09 01:10:05 +01:00
fileIncludeKind : ( FileIncludeKind as any ) [ reason . kind ] ,
2020-10-09 02:08:21 +02:00
} ) ;
2021-08-27 00:35:04 +02:00
const result = findSourceFileWorker ( fileName , isDefaultLib , ignoreNoDefaultLib , reason , packageId ) ;
2021-01-13 22:01:53 +01:00
tracing ? . pop ( ) ;
2020-10-09 02:08:21 +02:00
return result ;
}
2021-08-27 00:35:04 +02:00
function findSourceFileWorker ( fileName : string , isDefaultLib : boolean , ignoreNoDefaultLib : boolean , reason : FileIncludeReason , packageId : PackageId | undefined ) : SourceFile | undefined {
const path = toPath ( fileName ) ;
2019-07-11 00:21:24 +02:00
if ( useSourceOfProjectReferenceRedirect ) {
2021-08-27 00:35:04 +02:00
let source = getSourceOfProjectReferenceRedirect ( path ) ;
2019-10-29 18:49:14 +01:00
// If preserveSymlinks is true, module resolution wont jump the symlink
// but the resolved real path may be the .d.ts from project reference
// Note:: Currently we try the real path only if the
// file is from node_modules to avoid having to run real path on all file paths
if ( ! source &&
host . realpath &&
options . preserveSymlinks &&
isDeclarationFileName ( fileName ) &&
stringContains ( fileName , nodeModulesPathPart ) ) {
2021-08-27 00:35:04 +02:00
const realPath = toPath ( host . realpath ( fileName ) ) ;
if ( realPath !== path ) source = getSourceOfProjectReferenceRedirect ( realPath ) ;
2019-10-29 18:49:14 +01:00
}
2019-06-21 22:11:39 +02:00
if ( source ) {
2019-06-26 22:41:46 +02:00
const file = isString ( source ) ?
2021-08-27 00:35:04 +02:00
findSourceFile ( source , isDefaultLib , ignoreNoDefaultLib , reason , packageId ) :
2019-06-21 22:11:39 +02:00
undefined ;
2019-06-26 22:41:46 +02:00
if ( file ) addFileToFilesByName ( file , path , /*redirectedPath*/ undefined ) ;
return file ;
2019-06-21 22:11:39 +02:00
}
}
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 ) ;
2020-12-09 01:10:05 +01:00
addFileIncludeReason ( file || undefined , reason ) ;
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 ) {
const checkedName = file . fileName ;
2019-12-11 03:25:10 +01:00
const isRedirect = toPath ( checkedName ) !== toPath ( fileName ) ;
2018-07-25 00:24:52 +02:00
if ( isRedirect ) {
2019-12-11 03:25:10 +01:00
fileName = getProjectReferenceRedirect ( fileName ) || fileName ;
2018-07-25 00:24:52 +02:00
}
2019-05-21 18:32:17 +02:00
// Check if it differs only in drive letters its ok to ignore that error:
const checkedAbsolutePath = getNormalizedAbsolutePathWithoutRoot ( checkedName , currentDirectory ) ;
2019-12-11 03:25:10 +01:00
const inputAbsolutePath = getNormalizedAbsolutePathWithoutRoot ( fileName , currentDirectory ) ;
2019-05-21 18:32:17 +02:00
if ( checkedAbsolutePath !== inputAbsolutePath ) {
2020-12-09 01:10:05 +01:00
reportFileNamesDifferOnlyInCasingError ( fileName , file , reason ) ;
2018-07-25 00:24:52 +02:00
}
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 ;
2020-12-09 01:10:05 +01:00
if ( isReferencedFile ( reason ) && ! useSourceOfProjectReferenceRedirect ) {
2019-04-02 23:37:00 +02:00
const redirectProject = getProjectReferenceRedirectProject ( fileName ) ;
if ( redirectProject ) {
2020-06-02 20:49:21 +02:00
if ( outFile ( redirectProject . commandLine . options ) ) {
2019-04-02 23:37:00 +02:00
// Shouldnt create many to 1 mapping file in --out scenario
return undefined ;
}
const redirect = getProjectReferenceOutputName ( redirectProject , fileName ) ;
2018-05-08 00:12:50 +02:00
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
2019-08-09 20:15:20 +02:00
const file = host . getSourceFile (
fileName ,
2021-09-24 23:25:59 +02:00
getEmitScriptTarget ( options ) ,
2020-12-09 01:10:05 +01:00
hostErrorMessage = > addFilePreprocessingFileExplainingDiagnostic ( /*file*/ undefined , reason , Diagnostics . Cannot_read_file_0_Colon_1 , [ fileName , hostErrorMessage ] ) ,
2019-08-09 20:15:20 +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 ) ;
2020-12-09 01:10:05 +01:00
addFileIncludeReason ( dupFile , reason ) ;
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 ) ;
2019-12-11 03:25:10 +01:00
file . fileName = fileName ; // Ensure that source file has same name as what we were looking for
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 ;
2021-09-24 23:25:59 +02:00
// It's a _little odd_ that we can't set `impliedNodeFormat` until the program step - but it's the first and only time we have a resolution cache
// and a freshly made source file node on hand at the same time, and we need both to set the field. Persisting the resolution cache all the way
// to the check and emit steps would be bad - so we much prefer detecting and storing the format information on the source file node upfront.
file . impliedNodeFormat = getImpliedNodeFormatForFile ( file . resolvedPath , module ResolutionCache?.getPackageJsonInfoCache ( ) , host , options ) ;
2020-12-09 01:10:05 +01:00
addFileIncludeReason ( file , reason ) ;
2015-10-29 22:54:56 +01:00
2015-10-15 23:43:51 +02:00
if ( host . useCaseSensitiveFileNames ( ) ) {
2020-01-31 19:40:57 +01:00
const pathLowerCase = toFileNameLowerCase ( path ) ;
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 ) {
2020-12-09 01:10:05 +01:00
reportFileNamesDifferOnlyInCasingError ( fileName , existingFile , reason ) ;
2015-10-15 23:43:51 +02:00
}
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
}
2020-12-09 01:10:05 +01:00
function addFileIncludeReason ( file : SourceFile | undefined , reason : FileIncludeReason ) {
if ( file ) fileReasons . add ( file . path , reason ) ;
2019-08-09 20:15:20 +02: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 {
2019-04-02 23:37:00 +02:00
const referencedProject = getProjectReferenceRedirectProject ( fileName ) ;
return referencedProject && getProjectReferenceOutputName ( referencedProject , fileName ) ;
}
function getProjectReferenceRedirectProject ( fileName : string ) {
2020-01-08 18:54:32 +01:00
// Ignore dts or any json files
if ( ! resolvedProjectReferences || ! resolvedProjectReferences . length || fileExtensionIs ( fileName , Extension . Dts ) || fileExtensionIs ( fileName , Extension . Json ) ) {
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
2019-04-02 23:37:00 +02:00
return getResolvedProjectReferenceToRedirect ( fileName ) ;
}
function getProjectReferenceOutputName ( referencedProject : ResolvedProjectReference , fileName : string ) {
2020-06-02 20:49:21 +02:00
const out = outFile ( referencedProject . commandLine . options ) ;
2018-10-02 00:30:06 +02:00
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 ) {
2020-06-26 19:12:47 +02:00
mapFromFileToProjectReferenceRedirects = new Map ( ) ;
2020-10-19 21:59:59 +02:00
forEachResolvedProjectReference ( referencedProject = > {
2018-11-19 21:58:49 +01:00
// not input file from the referenced project, ignore
2020-10-19 21:59:59 +02:00
if ( toPath ( options . configFilePath ! ) !== referencedProject . sourceFile . path ) {
2018-11-19 21:58:49 +01:00
referencedProject . commandLine . fileNames . forEach ( f = >
2020-10-19 21:59:59 +02:00
mapFromFileToProjectReferenceRedirects ! . set ( toPath ( f ) , referencedProject . sourceFile . path ) ) ;
2018-11-19 21:58:49 +01:00
}
} ) ;
}
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 > (
2020-10-19 21:59:59 +02:00
cb : ( resolvedProjectReference : ResolvedProjectReference ) = > T | undefined
2018-10-02 23:10:19 +02:00
) : T | undefined {
2020-10-19 21:59:59 +02:00
return ts . forEachResolvedProjectReference ( resolvedProjectReferences , cb ) ;
2018-10-02 23:10:19 +02:00
}
2021-08-27 00:35:04 +02:00
function getSourceOfProjectReferenceRedirect ( path : Path ) {
if ( ! isDeclarationFileName ( path ) ) return undefined ;
2019-06-21 22:11:39 +02:00
if ( mapFromToProjectReferenceRedirectSource === undefined ) {
2020-06-26 01:03:25 +02:00
mapFromToProjectReferenceRedirectSource = new Map ( ) ;
2019-06-21 22:11:39 +02:00
forEachResolvedProjectReference ( resolvedRef = > {
2020-10-19 21:59:59 +02:00
const out = outFile ( resolvedRef . commandLine . options ) ;
if ( out ) {
// Dont know which source file it means so return true?
const outputDts = changeExtension ( out , Extension . Dts ) ;
mapFromToProjectReferenceRedirectSource ! . set ( toPath ( outputDts ) , true ) ;
}
else {
2020-12-07 20:53:22 +01:00
const getCommonSourceDirectory = memoize ( ( ) = > getCommonSourceDirectoryOfConfig ( resolvedRef . commandLine , ! host . useCaseSensitiveFileNames ( ) ) ) ;
2020-10-19 21:59:59 +02:00
forEach ( resolvedRef . commandLine . fileNames , fileName = > {
if ( ! fileExtensionIs ( fileName , Extension . Dts ) && ! fileExtensionIs ( fileName , Extension . Json ) ) {
2020-12-07 20:53:22 +01:00
const outputDts = getOutputDeclarationFileName ( fileName , resolvedRef . commandLine , ! host . useCaseSensitiveFileNames ( ) , getCommonSourceDirectory ) ;
2020-10-19 21:59:59 +02:00
mapFromToProjectReferenceRedirectSource ! . set ( toPath ( outputDts ) , fileName ) ;
}
} ) ;
2019-06-21 22:11:39 +02:00
}
} ) ;
}
2021-08-27 00:35:04 +02:00
return mapFromToProjectReferenceRedirectSource . get ( path ) ;
2019-06-21 22:11:39 +02:00
}
2019-07-01 23:29:32 +02:00
function isSourceOfProjectReferenceRedirect ( fileName : string ) {
2019-07-11 00:21:24 +02:00
return useSourceOfProjectReferenceRedirect && ! ! getResolvedProjectReferenceToRedirect ( fileName ) ;
2019-07-01 23:29:32 +02:00
}
2018-10-02 23:10:19 +02:00
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 ) {
2019-08-09 20:15:20 +02:00
forEach ( file . referencedFiles , ( ref , index ) = > {
processSourceFile (
2020-12-09 01:10:05 +01:00
resolveTripleslashReference ( ref . fileName , file . fileName ) ,
2019-08-09 20:15:20 +02:00
isDefaultLib ,
/*ignoreNoDefaultLib*/ false ,
/*packageId*/ undefined ,
2020-12-09 01:10:05 +01:00
{ kind : FileIncludeKind.ReferenceFile , file : file.path , index , }
2019-08-09 20:15:20 +02:00
) ;
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.
2020-01-31 19:40:57 +01:00
const typeDirectives = map ( file . typeReferenceDirectives , ref = > toFileNameLowerCase ( ref . fileName ) ) ;
2018-05-08 00:12:50 +02:00
if ( ! typeDirectives ) {
return ;
}
2020-10-06 20:23:05 +02:00
const resolutions = resolveTypeReferenceDirectiveNamesWorker ( typeDirectives , file ) ;
2020-12-09 01:10:05 +01:00
for ( let index = 0 ; index < typeDirectives . length ; index ++ ) {
const ref = file . typeReferenceDirectives [ index ] ;
const resolvedTypeReferenceDirective = resolutions [ index ] ;
2016-04-01 21:41:01 +02:00
// store resolved type directive on the file
2020-01-31 19:40:57 +01:00
const fileName = toFileNameLowerCase ( ref . fileName ) ;
2016-08-15 16:40:25 +02:00
setResolvedTypeReferenceDirective ( file , fileName , resolvedTypeReferenceDirective ) ;
2020-12-09 01:10:05 +01:00
processTypeReferenceDirective ( fileName , resolvedTypeReferenceDirective , { kind : FileIncludeKind.TypeReferenceDirective , file : file.path , index , } ) ;
2016-04-06 22:49:25 +02:00
}
}
2019-08-09 20:15:20 +02:00
function processTypeReferenceDirective (
typeReferenceDirective : string ,
2020-12-09 01:10:05 +01:00
resolvedTypeReferenceDirective : ResolvedTypeReferenceDirective | undefined ,
reason : FileIncludeReason
2019-08-09 20:15:20 +02:00
) : void {
2021-01-13 22:01:53 +01:00
tracing ? . push ( tracing . Phase . Program , "processTypeReferenceDirective" , { directive : typeReferenceDirective , hasResolved : ! ! resolveModuleNamesReusingOldState , refKind : reason.kind , refPath : isReferencedFile ( reason ) ? reason.file : undefined } ) ;
2020-12-09 01:10:05 +01:00
processTypeReferenceDirectiveWorker ( typeReferenceDirective , resolvedTypeReferenceDirective , reason ) ;
2021-01-13 22:01:53 +01:00
tracing ? . pop ( ) ;
2020-10-09 02:08:21 +02:00
}
function processTypeReferenceDirectiveWorker (
typeReferenceDirective : string ,
2020-12-09 01:10:05 +01:00
resolvedTypeReferenceDirective : ResolvedTypeReferenceDirective | undefined ,
reason : FileIncludeReason
2020-10-09 02:08:21 +02:00
) : void {
2016-04-06 22:49:25 +02:00
// 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
2020-12-09 01:10:05 +01:00
processSourceFile ( resolvedTypeReferenceDirective . resolvedFileName ! , /*isDefaultLib*/ false , /*ignoreNoDefaultLib*/ false , resolvedTypeReferenceDirective . packageId , reason ) ; // 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 ! ) ;
2019-12-11 03:25:10 +01:00
const existingFile = getSourceFile ( previousResolution . resolvedFileName ! ) ! ;
if ( otherFileText !== existingFile . text ) {
2020-12-09 01:10:05 +01:00
addFilePreprocessingFileExplainingDiagnostic (
existingFile ,
reason ,
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 ]
2019-08-09 20:15:20 +02:00
) ;
2016-11-09 16:41:25 +01:00
}
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
2020-12-09 01:10:05 +01:00
processSourceFile ( resolvedTypeReferenceDirective . resolvedFileName ! , /*isDefaultLib*/ false , /*ignoreNoDefaultLib*/ false , resolvedTypeReferenceDirective . packageId , reason ) ;
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 {
2020-12-09 01:10:05 +01:00
addFilePreprocessingFileExplainingDiagnostic ( /*file*/ undefined , reason , Diagnostics . Cannot_find_type_definition_file_for_0 , [ typeReferenceDirective ] ) ;
2016-04-06 22:49:25 +02:00
}
2016-02-23 21:48:31 +01:00
2016-04-06 22:49:25 +02:00
if ( saveResolution ) {
2016-12-05 23:13:32 +01:00
resolvedTypeReferenceDirectives . set ( typeReferenceDirective , resolvedTypeReferenceDirective ) ;
2016-04-06 22:49:25 +02:00
}
}
2021-09-15 22:25:08 +02:00
function pathForLibFile ( libFileName : string ) : string {
2021-09-24 18:46:29 +02:00
// Support resolving to lib.dom.d.ts -> @typescript/lib-dom, and
// lib.dom.iterable.d.ts -> @typescript/lib-dom/iterable
// lib.es2015.symbol.wellknown.d.ts -> @typescript/lib-es2015/symbol-wellknown
2021-09-15 22:25:08 +02:00
const components = libFileName . split ( "." ) ;
let path = components [ 1 ] ;
let i = 2 ;
while ( components [ i ] && components [ i ] !== "d" ) {
path += ( i === 2 ? "/" : "-" ) + components [ i ] ;
i ++ ;
}
const resolveFrom = combinePaths ( currentDirectory , ` __lib_node_modules_lookup_ ${ libFileName } __.ts ` ) ;
2021-09-24 18:46:29 +02:00
const localOverrideModuleResult = resolveModuleName ( "@typescript/lib-" + path , resolveFrom , { module Resolution : ModuleResolutionKind . NodeJs } , host , module ResolutionCache ) ;
2021-09-15 22:25:08 +02:00
if ( localOverrideModuleResult ? . resolvedModule ) {
return localOverrideModuleResult . resolvedModule . resolvedFileName ;
}
return combinePaths ( defaultLibraryPath , libFileName ) ;
}
2018-05-03 20:00:10 +02:00
function processLibReferenceDirectives ( file : SourceFile ) {
2020-12-09 01:10:05 +01:00
forEach ( file . libReferenceDirectives , ( libReference , index ) = > {
2020-01-31 19:40:57 +01:00
const libName = toFileNameLowerCase ( libReference . fileName ) ;
2018-05-03 20:00:10 +02:00
const libFileName = libMap . get ( libName ) ;
if ( libFileName ) {
// we ignore any 'no-default-lib' reference set on this file.
2021-09-15 22:25:08 +02:00
processRootFile ( pathForLibFile ( libFileName ) , /*isDefaultLib*/ true , /*ignoreNoDefaultLib*/ true , { kind : FileIncludeKind.LibReferenceDirective , file : file.path , index , } ) ;
2018-05-03 20:00:10 +02:00
}
else {
const unqualifiedLibName = removeSuffix ( removePrefix ( libName , "lib." ) , ".d.ts" ) ;
const suggestion = getSpellingSuggestion ( unqualifiedLibName , libs , identity ) ;
2020-12-09 01:10:05 +01:00
const diagnostic = suggestion ? Diagnostics.Cannot_find_lib_definition_for_0_Did_you_mean_1 : Diagnostics.Cannot_find_lib_definition_for_0 ;
( fileProcessingDiagnostics || = [ ] ) . push ( {
kind : FilePreprocessingDiagnosticsKind.FilePreprocessingReferencedDiagnostic ,
reason : { kind : FileIncludeKind.LibReferenceDirective , file : file.path , index , } ,
diagnostic ,
args : [ libName , suggestion ]
} ) ;
2018-05-03 20:00:10 +02: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 ) ;
2020-10-31 00:16:23 +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.
2020-10-31 00:16:23 +01:00
const module Names = getModuleNames ( file ) ;
2020-10-06 20:23:05 +02:00
const resolutions = resolveModuleNamesReusingOldState ( module Names , file ) ;
2016-10-13 22:02:22 +02:00
Debug . assert ( resolutions . length === module Names.length ) ;
2021-05-03 20:35:21 +02:00
const optionsForFile = ( useSourceOfProjectReferenceRedirect ? getRedirectReferenceForResolution ( file ) ? . commandLine.options : undefined ) || options ;
2020-12-09 01:10:05 +01:00
for ( let index = 0 ; index < module Names.length ; index ++ ) {
const resolution = resolutions [ index ] ;
2021-09-24 23:25:59 +02:00
setResolvedModule ( file , module Names [ index ] , resolution , getModeForResolutionAtIndex ( file , index ) ) ;
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
2021-05-03 20:35:21 +02:00
&& ! getResolutionDiagnostic ( optionsForFile , resolution )
&& ! optionsForFile . noResolve
2020-12-09 01:10:05 +01:00
&& index < file . imports . length
2017-09-19 21:42:29 +02:00
&& ! elideImport
2021-05-03 20:35:21 +02:00
&& ! ( isJsFile && ! getAllowJSCompilerOption ( optionsForFile ) )
2020-12-09 01:10:05 +01:00
&& ( isInJSFile ( file . imports [ index ] ) || ! ( file . imports [ index ] . 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 ) {
2019-08-09 20:15:20 +02:00
findSourceFile (
resolvedFileName ,
/*isDefaultLib*/ false ,
/*ignoreNoDefaultLib*/ false ,
2020-12-09 01:10:05 +01:00
{ kind : FileIncludeKind.Import , file : file.path , index , } ,
resolution . packageId ,
2019-08-09 20:15:20 +02:00
) ;
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
}
2019-08-08 20:30:18 +02:00
function checkSourceFilesBelongToPath ( sourceFiles : readonly 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 ) {
2020-12-09 01:10:05 +01:00
addProgramDiagnosticExplainingFile (
2019-08-09 20:15:20 +02:00
sourceFile ,
Diagnostics . File_0_is_not_under_rootDir_1_rootDir_is_expected_to_contain_all_source_files ,
2020-12-09 01:10:05 +01:00
[ sourceFile . fileName , rootDirectory ]
2019-08-09 20:15:20 +02:00
) ;
2018-08-28 02:03:30 +02:00
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 ) {
2020-06-26 19:12:47 +02:00
projectReferenceRedirects = new Map ( ) ;
2018-09-19 01:21:05 +02:00
}
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 ;
}
2019-04-23 23:02:08 +02:00
let commandLine : ParsedCommandLine | undefined ;
let sourceFile : JsonSourceFile | undefined ;
if ( host . getParsedCommandLine ) {
commandLine = host . getParsedCommandLine ( refPath ) ;
if ( ! commandLine ) {
2019-04-26 22:51:18 +02:00
addFileToFilesByName ( /*sourceFile*/ undefined , sourceFilePath , /*redirectedPath*/ undefined ) ;
2019-04-23 23:02:08 +02:00
projectReferenceRedirects . set ( sourceFilePath , false ) ;
return undefined ;
}
2020-02-25 03:20:58 +01:00
sourceFile = Debug . checkDefined ( commandLine . options . configFile ) ;
2019-05-06 21:31:20 +02:00
Debug . assert ( ! sourceFile . path || sourceFile . path === sourceFilePath ) ;
2019-04-26 22:51:18 +02:00
addFileToFilesByName ( sourceFile , sourceFilePath , /*redirectedPath*/ undefined ) ;
2019-04-23 23:02:08 +02:00
}
else {
// An absolute path pointing to the containing directory of the config file
const basePath = getNormalizedAbsolutePath ( getDirectoryPath ( refPath ) , host . getCurrentDirectory ( ) ) ;
sourceFile = host . getSourceFile ( refPath , ScriptTarget . JSON ) as JsonSourceFile | undefined ;
addFileToFilesByName ( sourceFile , sourceFilePath , /*redirectedPath*/ undefined ) ;
if ( sourceFile === undefined ) {
projectReferenceRedirects . set ( sourceFilePath , false ) ;
return undefined ;
}
commandLine = parseJsonSourceFileConfigFileContent ( sourceFile , configParsingHost , basePath , /*existingOptions*/ undefined , refPath ) ;
2018-05-08 00:12:50 +02:00
}
2019-12-11 03:25:10 +01:00
sourceFile . fileName = refPath ;
2019-05-06 17:17:24 +02:00
sourceFile . path = sourceFilePath ;
sourceFile . resolvedPath = sourceFilePath ;
sourceFile . originalFileName = 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() {
2021-11-16 21:53:37 +01:00
const isNightly = stringContains ( version , "-dev" ) || stringContains ( version , "-insiders" ) ;
2021-10-29 20:46:19 +02:00
if ( ! isNightly ) {
if ( getEmitModuleKind ( options ) === ModuleKind . Node12 ) {
createOptionValueDiagnostic ( "module" , Diagnostics . Compiler_option_0_of_value_1_is_unstable_Use_nightly_TypeScript_to_silence_this_error_Try_updating_with_npm_install_D_typescript_next , "module" , "node12" ) ;
}
else if ( getEmitModuleKind ( options ) === ModuleKind . NodeNext ) {
createOptionValueDiagnostic ( "module" , Diagnostics . Compiler_option_0_of_value_1_is_unstable_Use_nightly_TypeScript_to_silence_this_error_Try_updating_with_npm_install_D_typescript_next , "module" , "nodenext" ) ;
}
else if ( getEmitModuleResolutionKind ( options ) === ModuleResolutionKind . Node12 ) {
createOptionValueDiagnostic ( "moduleResolution" , Diagnostics . Compiler_option_0_of_value_1_is_unstable_Use_nightly_TypeScript_to_silence_this_error_Try_updating_with_npm_install_D_typescript_next , "moduleResolution" , "node12" ) ;
}
else if ( getEmitModuleResolutionKind ( options ) === ModuleResolutionKind . NodeNext ) {
createOptionValueDiagnostic ( "moduleResolution" , Diagnostics . Compiler_option_0_of_value_1_is_unstable_Use_nightly_TypeScript_to_silence_this_error_Try_updating_with_npm_install_D_typescript_next , "moduleResolution" , "nodenext" ) ;
}
}
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" ) ;
}
2021-06-18 00:12:19 +02:00
if ( options . exactOptionalPropertyTypes && ! getStrictOptionValue ( options , "strictNullChecks" ) ) {
createDiagnosticForOptionName ( Diagnostics . Option_0_cannot_be_specified_without_specifying_option_1 , "exactOptionalPropertyTypes" , "strictNullChecks" ) ;
2021-06-01 23:52:16 +02:00
}
2018-04-24 20:48:46 +02:00
2015-05-19 06:49:41 +02:00
if ( options . 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
}
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
}
}
2020-06-02 20:49:21 +02:00
const outputFile = outFile ( options ) ;
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" ) ;
}
}
2020-06-02 20:49:21 +02:00
else if ( options . incremental && ! outputFile && ! options . configFilePath ) {
2019-03-25 20:37:55 +01:00
programDiagnostics . add ( createCompilerDiagnostic ( Diagnostics . Option_incremental_can_only_be_specified_using_tsconfig_emitting_to_single_file_or_when_option_tsBuildInfoFile_is_specified ) ) ;
}
2019-03-01 18:58:16 +01:00
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 ) {
2020-06-26 19:12:47 +02:00
const rootPaths = new Set ( rootNames . map ( toPath ) ) ;
2019-03-14 18:58:57 +01:00
for ( const file of files ) {
2019-05-01 19:14:11 +02:00
// Ignore file that is not emitted
2019-11-27 22:47:19 +01:00
if ( sourceFileMayBeEmitted ( file , program ) && ! rootPaths . has ( file . path ) ) {
2020-12-09 01:10:05 +01:00
addProgramDiagnosticExplainingFile (
2019-08-09 20:15:20 +02:00
file ,
Diagnostics . File_0_is_not_listed_within_the_file_list_of_project_1_Projects_must_list_all_files_or_use_an_include_pattern ,
2020-12-09 01:10:05 +01:00
[ file . fileName , options . configFilePath || "" ]
2019-08-09 20:15:20 +02:00
) ;
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 ) ) {
2019-11-20 19:55:40 +01:00
createDiagnosticForOptionPathKeyValue ( key , i , Diagnostics . Substitution_0_in_pattern_1_can_have_at_most_one_Asterisk_character , subst , key ) ;
2016-04-14 06:16:39 +02:00
}
2020-09-11 21:58:40 +02:00
if ( ! options . baseUrl && ! pathIsRelative ( subst ) && ! pathIsAbsolute ( subst ) ) {
createDiagnosticForOptionPathKeyValue ( key , i , Diagnostics . Non_relative_paths_are_not_allowed_when_baseUrl_is_not_set_Did_you_forget_a_leading_Slash ) ;
}
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
}
2020-06-02 20:49:21 +02:00
if ( outputFile ) {
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
}
2021-09-24 23:25:59 +02:00
const languageVersion = getEmitScriptTarget ( options ) ;
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
}
fix: const enums + isolatedModules emit invalid code (#41933)
* chore: failing test for const enums and isolatedModules
* fix: const enums + isolatedModules emit invalid code
In `isolatedModules` mode, the compiler does not inline const enums,
but also decides not to `import` them, leaving invalid code that
throws a `ReferenceError` at runtime.
This code:
```
import { SomeEnum } from './bar';
sink(SomeEnum.VALUE);
```
..should compile to either:
```
var { SomeEnum } = require('./bar');
sink(SomeEnum.VALUE);
```
..or (with const enum inlining):
```
sink(1 /* VALUE */);
```
..but actually compiles to:
```
sink(SomeEnum.VALUE);
```
..with no imports, which throws a ReferenceError at runtime.
---
The compiler has already realised that the symbol is a referenced const
enum, it just doesn't use this information when it comes to deciding
whether to emit an import. This commit additionally checks that
information, if we are compiling in isolatedModules mode.
---
In my opinion, this is not the correct fix, but it is the simplest. In
`isolatedModules` mode, `const enum` should probably be a compile error
(because there are no benefits and the complexity is high, and,
apparently, buggy). If not, the compiler should not be checking whether
something is a const enum, and should just be treating it as a regular
enum, and checking as if it was?
Fixes #40499.
* chore: extra test for type-only
* feat: explicitly ban --isolatedModules --preserveConstEnums false
* feat: isolatedModules implies preserveConstEnum
* Update src/compiler/diagnosticMessages.json
Co-authored-by: Andrew Branch <andrewbranch@users.noreply.github.com>
* chore: compiler test for argument incompatibility
* Add and fix test for namespace import of const enum
* Fix additional bug with reexport of const-enum-only module
Co-authored-by: Andrew Branch <andrewbranch@users.noreply.github.com>
Co-authored-by: Andrew Branch <andrew@wheream.io>
2021-01-14 00:51:08 +01:00
if ( options . preserveConstEnums === false ) {
createDiagnosticForOptionName ( Diagnostics . Option_preserveConstEnums_cannot_be_disabled_when_isolatedModules_is_enabled , "preserveConstEnums" , "isolatedModules" ) ;
}
2019-05-23 20:09:28 +02:00
const firstNonExternalModuleSourceFile = find ( files , f = > ! isExternalModule ( f ) && ! isSourceFileJS ( 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 ) ;
2020-08-13 23:03:17 +02:00
programDiagnostics . add ( createFileDiagnostic ( firstNonExternalModuleSourceFile , span . start , span . length ,
Diagnostics . _0_cannot_be_compiled_under_isolatedModules_because_it_is_considered_a_global_script_file_Add_an_import_export_or_an_empty_export_statement_to_make_it_a_module , getBaseFileName ( firstNonExternalModuleSourceFile . fileName ) ) ) ;
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
2020-06-02 20:49:21 +02:00
if ( outputFile && ! 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 ) {
2021-10-27 21:30:06 +02:00
if ( getEmitModuleResolutionKind ( options ) !== ModuleResolutionKind . NodeJs &&
getEmitModuleResolutionKind ( options ) !== ModuleResolutionKind . Node12 &&
getEmitModuleResolutionKind ( options ) !== ModuleResolutionKind . NodeNext ) {
2018-04-06 00:27:44 +02:00
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
}
2021-03-19 23:52:04 +01:00
// there has to be common source directory if user specified --outdir || --rootDir || --sourceRoot
2015-10-22 00:27:33 +02:00
// 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
2021-03-19 23:52:04 +01:00
options . rootDir || // there is --rootDir specified
2015-10-22 00:27:33 +02:00
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
}
2019-09-26 22:25:05 +02:00
if ( options . useDefineForClassFields && languageVersion === ScriptTarget . ES3 ) {
createDiagnosticForOptionName ( Diagnostics . Option_0_cannot_be_specified_when_option_target_is_ES3 , "useDefineForClassFields" ) ;
}
2020-09-01 19:16:08 +02:00
if ( options . checkJs && ! getAllowJSCompilerOption ( options ) ) {
2017-03-07 22:49:52 +01:00
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
}
2020-09-11 19:44:52 +02:00
if ( options . jsx === JsxEmit . ReactJSX || options . jsx === JsxEmit . ReactJSXDev ) {
createDiagnosticForOptionName ( Diagnostics . Option_0_cannot_be_specified_when_option_jsx_is_1 , "jsxFactory" , inverseJsxOptionMap . get ( "" + options . jsx ) ) ;
}
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
}
2020-06-18 09:22:32 +02:00
if ( options . jsxFragmentFactory ) {
if ( ! options . jsxFactory ) {
createDiagnosticForOptionName ( Diagnostics . Option_0_cannot_be_specified_without_specifying_option_1 , "jsxFragmentFactory" , "jsxFactory" ) ;
}
2020-09-11 19:44:52 +02:00
if ( options . jsx === JsxEmit . ReactJSX || options . jsx === JsxEmit . ReactJSXDev ) {
createDiagnosticForOptionName ( Diagnostics . Option_0_cannot_be_specified_when_option_jsx_is_1 , "jsxFragmentFactory" , inverseJsxOptionMap . get ( "" + options . jsx ) ) ;
}
2020-06-18 09:22:32 +02:00
if ( ! parseIsolatedEntityName ( options . jsxFragmentFactory , languageVersion ) ) {
createOptionValueDiagnostic ( "jsxFragmentFactory" , Diagnostics . Invalid_value_for_jsxFragmentFactory_0_is_not_a_valid_identifier_or_qualified_name , options . jsxFragmentFactory ) ;
}
}
2020-09-11 19:44:52 +02:00
if ( options . reactNamespace ) {
if ( options . jsx === JsxEmit . ReactJSX || options . jsx === JsxEmit . ReactJSXDev ) {
createDiagnosticForOptionName ( Diagnostics . Option_0_cannot_be_specified_when_option_jsx_is_1 , "reactNamespace" , inverseJsxOptionMap . get ( "" + options . jsx ) ) ;
}
}
if ( options . jsxImportSource ) {
if ( options . jsx === JsxEmit . React ) {
createDiagnosticForOptionName ( Diagnostics . Option_0_cannot_be_specified_when_option_jsx_is_1 , "jsxImportSource" , inverseJsxOptionMap . get ( "" + options . jsx ) ) ;
}
}
2021-09-09 01:30:22 +02:00
if ( options . preserveValueImports && getEmitModuleKind ( options ) < ModuleKind . ES2015 ) {
createOptionValueDiagnostic ( "importsNotUsedAsValues" , Diagnostics . Option_preserveValueImports_can_only_be_used_when_module_is_set_to_es2015_or_later ) ;
}
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 ( ) ;
2020-06-26 19:12:47 +02:00
const emitFilesSeen = new Set < string > ( ) ;
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
2020-06-26 19:12:47 +02:00
function verifyEmitFilePath ( emitFileName : string | undefined , emitFilesSeen : Set < string > ) {
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
}
2020-01-31 19:40:57 +01:00
const emitFileKey = ! host . useCaseSensitiveFileNames ( ) ? toFileNameLowerCase ( emitFilePath ) : 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 {
2020-06-26 19:12:47 +02:00
emitFilesSeen . add ( emitFileKey ) ;
2015-10-12 21:25:13 +02:00
}
2015-10-30 21:22:23 +01:00
}
2015-10-12 21:25:13 +02:00
}
}
2020-12-09 01:10:05 +01:00
function createDiagnosticExplainingFile ( file : SourceFile | undefined , fileProcessingReason : FileIncludeReason | undefined , diagnostic : DiagnosticMessage , args : ( string | number | undefined ) [ ] | undefined ) : Diagnostic {
let fileIncludeReasons : DiagnosticMessageChain [ ] | undefined ;
let relatedInfo : Diagnostic [ ] | undefined ;
let locationReason = isReferencedFile ( fileProcessingReason ) ? fileProcessingReason : undefined ;
if ( file ) fileReasons . get ( file . path ) ? . forEach ( processReason ) ;
if ( fileProcessingReason ) processReason ( fileProcessingReason ) ;
// If we have location and there is only one reason file is in which is the location, dont add details for file include
if ( locationReason && fileIncludeReasons ? . length === 1 ) fileIncludeReasons = undefined ;
const location = locationReason && getReferencedFileLocation ( getSourceFileByPath , locationReason ) ;
const fileIncludeReasonDetails = fileIncludeReasons && chainDiagnosticMessages ( fileIncludeReasons , Diagnostics . The_file_is_in_the_program_because_Colon ) ;
const redirectInfo = file && explainIfFileIsRedirect ( file ) ;
const chain = chainDiagnosticMessages ( redirectInfo ? fileIncludeReasonDetails ? [ fileIncludeReasonDetails , . . . redirectInfo ] : redirectInfo : fileIncludeReasonDetails , diagnostic , . . . args || emptyArray ) ;
return location && isReferenceFileLocation ( location ) ?
createFileDiagnosticFromMessageChain ( location . file , location . pos , location . end - location . pos , chain , relatedInfo ) :
createCompilerDiagnosticFromMessageChain ( chain , relatedInfo ) ;
function processReason ( reason : FileIncludeReason ) {
( fileIncludeReasons || = [ ] ) . push ( fileIncludeReasonToDiagnostics ( program , reason ) ) ;
if ( ! locationReason && isReferencedFile ( reason ) ) {
// Report error at first reference file or file currently in processing and dont report in related information
locationReason = reason ;
}
else if ( locationReason !== reason ) {
relatedInfo = append ( relatedInfo , fileIncludeReasonToRelatedInformation ( reason ) ) ;
}
// Remove fileProcessingReason if its already included in fileReasons of the program
if ( reason === fileProcessingReason ) fileProcessingReason = undefined ;
}
}
function addFilePreprocessingFileExplainingDiagnostic ( file : SourceFile | undefined , fileProcessingReason : FileIncludeReason , diagnostic : DiagnosticMessage , args ? : ( string | number | undefined ) [ ] ) {
( fileProcessingDiagnostics || = [ ] ) . push ( {
kind : FilePreprocessingDiagnosticsKind.FilePreprocessingFileExplainingDiagnostic ,
file : file && file . path ,
fileProcessingReason ,
diagnostic ,
args
} ) ;
}
function addProgramDiagnosticExplainingFile ( file : SourceFile , diagnostic : DiagnosticMessage , args ? : ( string | number | undefined ) [ ] ) {
programDiagnostics . add ( createDiagnosticExplainingFile ( file , /*fileProcessingReason*/ undefined , diagnostic , args ) ) ;
}
function fileIncludeReasonToRelatedInformation ( reason : FileIncludeReason ) : DiagnosticWithLocation | undefined {
if ( isReferencedFile ( reason ) ) {
const referenceLocation = getReferencedFileLocation ( getSourceFileByPath , reason ) ;
let message : DiagnosticMessage ;
switch ( reason . kind ) {
case FileIncludeKind . Import :
message = Diagnostics . File_is_included_via_import_here ;
break ;
case FileIncludeKind . ReferenceFile :
message = Diagnostics . File_is_included_via_reference_here ;
break ;
case FileIncludeKind . TypeReferenceDirective :
message = Diagnostics . File_is_included_via_type_library_reference_here ;
break ;
case FileIncludeKind . LibReferenceDirective :
message = Diagnostics . File_is_included_via_library_reference_here ;
break ;
default :
Debug . assertNever ( reason ) ;
}
return isReferenceFileLocation ( referenceLocation ) ? createFileDiagnostic (
referenceLocation . file ,
referenceLocation . pos ,
referenceLocation . end - referenceLocation . pos ,
message ,
) : undefined ;
}
if ( ! options . configFile ) return undefined ;
let configFileNode : Node | undefined ;
let message : DiagnosticMessage ;
switch ( reason . kind ) {
case FileIncludeKind . RootFile :
if ( ! options . configFile . configFileSpecs ) return undefined ;
const fileName = getNormalizedAbsolutePath ( rootNames [ reason . index ] , currentDirectory ) ;
const matchedByFiles = getMatchedFileSpec ( program , fileName ) ;
if ( matchedByFiles ) {
configFileNode = getTsConfigPropArrayElementValue ( options . configFile , "files" , matchedByFiles ) ;
message = Diagnostics . File_is_matched_by_files_list_specified_here ;
break ;
}
const matchedByInclude = getMatchedIncludeSpec ( program , fileName ) ;
// Could be additional files specified as roots
if ( ! matchedByInclude ) return undefined ;
configFileNode = getTsConfigPropArrayElementValue ( options . configFile , "include" , matchedByInclude ) ;
message = Diagnostics . File_is_matched_by_include_pattern_specified_here ;
2019-12-11 03:25:10 +01:00
break ;
2020-12-09 01:10:05 +01:00
case FileIncludeKind . SourceFromProjectReference :
case FileIncludeKind . OutputFromProjectReference :
const referencedResolvedRef = Debug . checkDefined ( resolvedProjectReferences ? . [ reason . index ] ) ;
const referenceInfo = forEachProjectReference ( projectReferences , resolvedProjectReferences , ( resolvedRef , parent , index ) = >
resolvedRef === referencedResolvedRef ? { sourceFile : parent?.sourceFile || options . configFile ! , index } : undefined
) ;
if ( ! referenceInfo ) return undefined ;
const { sourceFile , index } = referenceInfo ;
const referencesSyntax = firstDefined ( getTsConfigPropArray ( sourceFile as TsConfigSourceFile , "references" ) ,
property = > isArrayLiteralExpression ( property . initializer ) ? property.initializer : undefined ) ;
return referencesSyntax && referencesSyntax . elements . length > index ?
createDiagnosticForNodeInSourceFile (
sourceFile ,
referencesSyntax . elements [ index ] ,
reason . kind === FileIncludeKind . OutputFromProjectReference ?
Diagnostics . File_is_output_from_referenced_project_specified_here :
Diagnostics . File_is_source_from_referenced_project_specified_here ,
) :
undefined ;
case FileIncludeKind . AutomaticTypeDirectiveFile :
if ( ! options . types ) return undefined ;
configFileNode = getOptionsSyntaxByArrayElementValue ( "types" , reason . typeReference ) ;
message = Diagnostics . File_is_entry_point_of_type_library_specified_here ;
2019-12-11 03:25:10 +01:00
break ;
2020-12-09 01:10:05 +01:00
case FileIncludeKind . LibFile :
if ( reason . index !== undefined ) {
configFileNode = getOptionsSyntaxByArrayElementValue ( "lib" , options . lib ! [ reason . index ] ) ;
message = Diagnostics . File_is_library_specified_here ;
break ;
}
2021-09-24 23:25:59 +02:00
const target = forEachEntry ( targetOptionDeclaration . type , ( value , key ) = > value === getEmitScriptTarget ( options ) ? key : undefined ) ;
2020-12-09 01:10:05 +01:00
configFileNode = target ? getOptionsSyntaxByValue ( "target" , target ) : undefined ;
message = Diagnostics . File_is_default_library_for_target_specified_here ;
2019-12-11 03:25:10 +01:00
break ;
default :
2020-12-09 01:10:05 +01:00
Debug . assertNever ( reason ) ;
2019-12-11 03:25:10 +01:00
}
2020-12-09 01:10:05 +01:00
return configFileNode && createDiagnosticForNodeInSourceFile (
options . configFile ,
configFileNode ,
message ,
2019-12-11 03:25:10 +01:00
) ;
2019-08-09 20:15:20 +02:00
}
2018-10-04 01:09:16 +02:00
function verifyProjectReferences() {
2020-06-18 20:05:37 +02:00
const buildInfoPath = ! options . suppressOutputPathCheck ? getTsBuildInfoEmitOutputFilePath ( options ) : undefined ;
2020-10-19 21:59:59 +02:00
forEachProjectReference ( projectReferences , resolvedProjectReferences , ( resolvedRef , parent , index ) = > {
2018-10-04 01:09:16 +02:00
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 ;
2020-06-18 20:05:37 +02:00
if ( ! options . composite || options . noEmit ) {
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 ) {
2020-06-18 20:05:37 +02:00
if ( ! options . composite ) createDiagnosticForReference ( parentFile , index , Diagnostics . Referenced_project_0_must_have_setting_composite_Colon_true , ref . path ) ;
if ( options . noEmit ) createDiagnosticForReference ( parentFile , index , Diagnostics . Referenced_project_0_may_not_disable_emit , ref . path ) ;
2018-10-30 23:22:00 +01:00
}
2018-10-04 01:09:16 +02:00
}
if ( ref . prepend ) {
2020-06-02 20:49:21 +02:00
const out = outFile ( options ) ;
2018-10-04 01:09:16 +02:00
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-09-18 23:02:41 +02:00
if ( ! parent && buildInfoPath && buildInfoPath === getTsBuildInfoEmitOutputFilePath ( options ) ) {
2019-02-28 01:15:09 +01:00
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
} ) ;
}
2020-09-11 21:58:40 +02:00
function createDiagnosticForOptionPathKeyValue ( key : string , valueIndex : number , message : DiagnosticMessage , arg0? : string | number , arg1? : string | number , arg2? : string | number ) {
2016-11-29 21:42:17 +01:00
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 ) ) ;
}
}
2020-12-09 01:10:05 +01:00
function getOptionsSyntaxByName ( name : string ) {
2016-11-29 21:42:17 +01:00
const compilerOptionsObjectLiteralSyntax = getCompilerOptionsObjectLiteralSyntax ( ) ;
2020-12-09 01:10:05 +01:00
return compilerOptionsObjectLiteralSyntax && getPropertyAssignment ( compilerOptionsObjectLiteralSyntax , name ) ;
}
function getOptionPathsSyntax() {
return getOptionsSyntaxByName ( "paths" ) || emptyArray ;
2018-05-08 00:12:50 +02:00
}
2020-12-09 01:10:05 +01:00
function getOptionsSyntaxByValue ( name : string , value : string ) {
const syntaxByName = getOptionsSyntaxByName ( name ) ;
return syntaxByName && firstDefined ( syntaxByName , property = > isStringLiteral ( property . initializer ) && property . initializer . text === value ? property.initializer : undefined ) ;
}
function getOptionsSyntaxByArrayElementValue ( name : string , value : string ) {
const compilerOptionsObjectLiteralSyntax = getCompilerOptionsObjectLiteralSyntax ( ) ;
return compilerOptionsObjectLiteralSyntax && getPropertyArrayElementValue ( compilerOptionsObjectLiteralSyntax , name , value ) ;
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
}
2021-10-29 20:46:19 +02:00
function createOptionValueDiagnostic ( option1 : string , message : DiagnosticMessage , arg0? : string , arg1? : string ) {
createDiagnosticForOption ( /*onKey*/ false , option1 , /*option2*/ undefined , message , arg0 , arg1 ) ;
2016-11-29 21:42:17 +01:00
}
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
}
}
2021-09-09 01:30:22 +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 ) {
2020-12-09 01:10:05 +01:00
_compilerOptionsObjectLiteralSyntax = false ;
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 ;
}
}
}
}
2020-12-09 01:10:05 +01:00
return _compilerOptionsObjectLiteralSyntax || undefined ;
2016-11-29 21:42:17 +01:00
}
2021-09-09 01:30:22 +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
2020-06-02 20:49:21 +02:00
const out = outFile ( options ) ;
2018-01-30 00:41:21 +01:00
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 ( ) ) ;
}
2021-09-24 23:25:59 +02:00
if ( fileExtensionIsOneOf ( filePath , supportedJSExtensionsFlat ) || 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 ;
}
2019-09-27 22:38:31 +02:00
2020-07-22 22:53:30 +02:00
function getSymlinkCache ( ) : SymlinkCache {
if ( host . getSymlinkCache ) {
return host . getSymlinkCache ( ) ;
2019-09-27 22:38:31 +02:00
}
2021-06-08 19:06:55 +02:00
if ( ! symlinks ) {
symlinks = createSymlinkCache ( currentDirectory , getCanonicalFileName ) ;
}
if ( files && resolvedTypeReferenceDirectives && ! symlinks . hasProcessedResolutions ( ) ) {
symlinks . setSymlinksFromResolutions ( files , resolvedTypeReferenceDirectives ) ;
}
return symlinks ;
2019-09-27 22:38:31 +02:00
}
2014-12-16 22:14:14 +01:00
}
2016-10-13 22:02:22 +02:00
2020-03-12 21:11:11 +01:00
interface HostForUseSourceOfProjectReferenceRedirect {
compilerHost : CompilerHost ;
2020-07-22 22:53:30 +02:00
getSymlinkCache : ( ) = > SymlinkCache ;
2020-03-12 21:11:11 +01:00
useSourceOfProjectReferenceRedirect : boolean ;
toPath ( fileName : string ) : Path ;
getResolvedProjectReferences ( ) : readonly ( ResolvedProjectReference | undefined ) [ ] | undefined ;
2021-08-27 00:35:04 +02:00
getSourceOfProjectReferenceRedirect ( path : Path ) : SourceOfProjectReferenceRedirect | undefined ;
2020-10-19 21:59:59 +02:00
forEachResolvedProjectReference < T > ( cb : ( resolvedProjectReference : ResolvedProjectReference ) = > T | undefined ) : T | undefined ;
2020-03-12 21:11:11 +01:00
}
function updateHostForUseSourceOfProjectReferenceRedirect ( host : HostForUseSourceOfProjectReferenceRedirect ) {
2020-06-26 19:12:47 +02:00
let setOfDeclarationDirectories : Set < Path > | undefined ;
2020-03-12 21:11:11 +01:00
const originalFileExists = host . compilerHost . fileExists ;
const originalDirectoryExists = host . compilerHost . directoryExists ;
const originalGetDirectories = host . compilerHost . getDirectories ;
const originalRealpath = host . compilerHost . realpath ;
2020-03-20 00:53:44 +01:00
if ( ! host . useSourceOfProjectReferenceRedirect ) return { onProgramCreateComplete : noop , fileExists } ;
2020-03-12 21:11:11 +01:00
2020-03-20 00:53:44 +01:00
host . compilerHost . fileExists = fileExists ;
2020-03-12 21:11:11 +01:00
2020-07-22 22:53:30 +02:00
let directoryExists ;
2020-03-12 21:11:11 +01:00
if ( originalDirectoryExists ) {
// This implementation of directoryExists checks if the directory being requested is
// directory of .d.ts file for the referenced Project.
// If it is it returns true irrespective of whether that directory exists on host
2020-07-22 22:53:30 +02:00
directoryExists = host . compilerHost . directoryExists = path = > {
2020-03-12 21:11:11 +01:00
if ( originalDirectoryExists . call ( host . compilerHost , path ) ) {
handleDirectoryCouldBeSymlink ( path ) ;
return true ;
}
if ( ! host . getResolvedProjectReferences ( ) ) return false ;
2020-06-26 19:12:47 +02:00
if ( ! setOfDeclarationDirectories ) {
setOfDeclarationDirectories = new Set ( ) ;
2020-03-12 21:11:11 +01:00
host . forEachResolvedProjectReference ( ref = > {
2020-06-02 20:49:21 +02:00
const out = outFile ( ref . commandLine . options ) ;
2020-03-12 21:11:11 +01:00
if ( out ) {
2020-06-26 19:12:47 +02:00
setOfDeclarationDirectories ! . add ( getDirectoryPath ( host . toPath ( out ) ) ) ;
2020-03-12 21:11:11 +01:00
}
else {
// Set declaration's in different locations only, if they are next to source the directory present doesnt change
const declarationDir = ref . commandLine . options . declarationDir || ref . commandLine . options . outDir ;
if ( declarationDir ) {
2020-06-26 19:12:47 +02:00
setOfDeclarationDirectories ! . add ( host . toPath ( declarationDir ) ) ;
2020-03-12 21:11:11 +01:00
}
}
} ) ;
}
return fileOrDirectoryExistsUsingSource ( path , /*isFile*/ false ) ;
} ;
}
if ( originalGetDirectories ) {
// Call getDirectories only if directory actually present on the host
// This is needed to ensure that we arent getting directories that we fake about presence for
host . compilerHost . getDirectories = path = >
! host . getResolvedProjectReferences ( ) || ( originalDirectoryExists && originalDirectoryExists . call ( host . compilerHost , path ) ) ?
originalGetDirectories . call ( host . compilerHost , path ) :
[ ] ;
}
// This is something we keep for life time of the host
if ( originalRealpath ) {
host . compilerHost . realpath = s = >
2020-07-22 22:53:30 +02:00
host . getSymlinkCache ( ) . getSymlinkedFiles ( ) ? . get ( host . toPath ( s ) ) ||
2020-03-12 21:11:11 +01:00
originalRealpath . call ( host . compilerHost , s ) ;
}
2020-07-22 22:53:30 +02:00
return { onProgramCreateComplete , fileExists , directoryExists } ;
2020-03-12 21:11:11 +01:00
function onProgramCreateComplete() {
host . compilerHost . fileExists = originalFileExists ;
host . compilerHost . directoryExists = originalDirectoryExists ;
host . compilerHost . getDirectories = originalGetDirectories ;
// DO not revert realpath as it could be used later
}
2020-03-20 00:53:44 +01:00
// This implementation of fileExists checks if the file being requested is
// .d.ts file for the referenced Project.
// If it is it returns true irrespective of whether that file exists on host
function fileExists ( file : string ) {
if ( originalFileExists . call ( host . compilerHost , file ) ) return true ;
if ( ! host . getResolvedProjectReferences ( ) ) return false ;
if ( ! isDeclarationFileName ( file ) ) return false ;
// Project references go to source file instead of .d.ts file
return fileOrDirectoryExistsUsingSource ( file , /*isFile*/ true ) ;
}
2020-03-12 21:11:11 +01:00
function fileExistsIfProjectReferenceDts ( file : string ) {
2021-08-27 00:35:04 +02:00
const source = host . getSourceOfProjectReferenceRedirect ( host . toPath ( file ) ) ;
2020-03-12 21:11:11 +01:00
return source !== undefined ?
2021-08-27 00:35:04 +02:00
isString ( source ) ? originalFileExists . call ( host . compilerHost , source ) as boolean : true :
2020-03-12 21:11:11 +01:00
undefined ;
}
function directoryExistsIfProjectReferenceDeclDir ( dir : string ) {
const dirPath = host . toPath ( dir ) ;
const dirPathWithTrailingDirectorySeparator = ` ${ dirPath } ${ directorySeparator } ` ;
return forEachKey (
2020-06-26 19:12:47 +02:00
setOfDeclarationDirectories ! ,
2020-03-12 21:11:11 +01:00
declDirPath = > dirPath === declDirPath ||
// Any parent directory of declaration dir
startsWith ( declDirPath , dirPathWithTrailingDirectorySeparator ) ||
// Any directory inside declaration dir
startsWith ( dirPath , ` ${ declDirPath } / ` )
) ;
}
function handleDirectoryCouldBeSymlink ( directory : string ) {
2021-01-12 23:04:03 +01:00
if ( ! host . getResolvedProjectReferences ( ) || containsIgnoredPath ( directory ) ) return ;
2020-03-12 21:11:11 +01:00
// Because we already watch node_modules, handle symlinks in there
if ( ! originalRealpath || ! stringContains ( directory , nodeModulesPathPart ) ) return ;
2020-07-22 22:53:30 +02:00
const symlinkCache = host . getSymlinkCache ( ) ;
2020-03-12 21:11:11 +01:00
const directoryPath = ensureTrailingDirectorySeparator ( host . toPath ( directory ) ) ;
2020-07-22 22:53:30 +02:00
if ( symlinkCache . getSymlinkedDirectories ( ) ? . has ( directoryPath ) ) return ;
2020-03-12 21:11:11 +01:00
const real = normalizePath ( originalRealpath . call ( host . compilerHost , directory ) ) ;
let realPath : Path ;
if ( real === directory ||
( realPath = ensureTrailingDirectorySeparator ( host . toPath ( real ) ) ) === directoryPath ) {
// not symlinked
2020-07-22 22:53:30 +02:00
symlinkCache . setSymlinkedDirectory ( directoryPath , false ) ;
2020-03-12 21:11:11 +01:00
return ;
}
2021-01-20 21:15:36 +01:00
symlinkCache . setSymlinkedDirectory ( directory , {
2020-03-12 21:11:11 +01:00
real : ensureTrailingDirectorySeparator ( real ) ,
realPath
} ) ;
}
function fileOrDirectoryExistsUsingSource ( fileOrDirectory : string , isFile : boolean ) : boolean {
const fileOrDirectoryExistsUsingSource = isFile ?
( file : string ) = > fileExistsIfProjectReferenceDts ( file ) :
( dir : string ) = > directoryExistsIfProjectReferenceDeclDir ( dir ) ;
// Check current directory or file
const result = fileOrDirectoryExistsUsingSource ( fileOrDirectory ) ;
if ( result !== undefined ) return result ;
2020-07-22 22:53:30 +02:00
const symlinkCache = host . getSymlinkCache ( ) ;
const symlinkedDirectories = symlinkCache . getSymlinkedDirectories ( ) ;
2020-03-12 21:11:11 +01:00
if ( ! symlinkedDirectories ) return false ;
const fileOrDirectoryPath = host . toPath ( fileOrDirectory ) ;
if ( ! stringContains ( fileOrDirectoryPath , nodeModulesPathPart ) ) return false ;
2020-07-22 22:53:30 +02:00
if ( isFile && symlinkCache . getSymlinkedFiles ( ) ? . has ( fileOrDirectoryPath ) ) return true ;
2020-03-12 21:11:11 +01:00
// If it contains node_modules check if its one of the symlinked path we know of
return firstDefinedIterator (
symlinkedDirectories . entries ( ) ,
( [ directoryPath , symlinkedDirectory ] ) = > {
if ( ! symlinkedDirectory || ! startsWith ( fileOrDirectoryPath , directoryPath ) ) return undefined ;
const result = fileOrDirectoryExistsUsingSource ( fileOrDirectoryPath . replace ( directoryPath , symlinkedDirectory . realPath ) ) ;
if ( isFile && result ) {
// Store the real path for the file'
const absolutePath = getNormalizedAbsolutePath ( fileOrDirectory , host . compilerHost . getCurrentDirectory ( ) ) ;
2020-07-22 22:53:30 +02:00
symlinkCache . setSymlinkedFile (
2020-03-12 21:11:11 +01:00
fileOrDirectoryPath ,
` ${ symlinkedDirectory . real } ${ absolutePath . replace ( new RegExp ( directoryPath , "i" ) , "" ) } `
) ;
}
return result ;
}
) || false ;
}
}
2019-12-13 04:51:18 +01:00
/*@internal*/
2020-06-02 21:38:40 +02:00
export const emitSkippedWithNoDiagnostics : EmitResult = { diagnostics : emptyArray , sourceMaps : undefined , emittedFiles : undefined , emitSkipped : true } ;
/*@internal*/
2020-12-09 01:10:05 +01:00
export function handleNoEmitOptions < T extends BuilderProgram > (
program : Program | T ,
2020-06-02 21:38:40 +02:00
sourceFile : SourceFile | undefined ,
writeFile : WriteFileCallback | undefined ,
cancellationToken : CancellationToken | undefined
) : EmitResult | undefined {
2019-12-13 04:51:18 +01:00
const options = program . getCompilerOptions ( ) ;
2020-06-18 20:05:37 +02:00
if ( options . noEmit ) {
// Cache the semantic diagnostics
program . getSemanticDiagnostics ( sourceFile , cancellationToken ) ;
return sourceFile || outFile ( options ) ?
emitSkippedWithNoDiagnostics :
program . emitBuildInfo ( writeFile , cancellationToken ) ;
}
2019-12-13 04:51:18 +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 ) return undefined ;
let diagnostics : readonly Diagnostic [ ] = [
. . . program . getOptionsDiagnostics ( cancellationToken ) ,
. . . program . getSyntacticDiagnostics ( sourceFile , cancellationToken ) ,
. . . program . getGlobalDiagnostics ( cancellationToken ) ,
. . . program . getSemanticDiagnostics ( sourceFile , cancellationToken )
] ;
if ( diagnostics . length === 0 && getEmitDeclarations ( program . getCompilerOptions ( ) ) ) {
diagnostics = program . getDeclarationDiagnostics ( /*sourceFile*/ undefined , cancellationToken ) ;
}
2020-06-02 21:38:40 +02:00
if ( ! diagnostics . length ) return undefined ;
let emittedFiles : string [ ] | undefined ;
if ( ! sourceFile && ! outFile ( options ) ) {
const emitResult = program . emitBuildInfo ( writeFile , cancellationToken ) ;
if ( emitResult . diagnostics ) diagnostics = [ . . . diagnostics , . . . emitResult . diagnostics ] ;
emittedFiles = emitResult . emittedFiles ;
}
return { diagnostics , sourceMaps : undefined , emittedFiles , emitSkipped : true } ;
2019-12-13 04:51:18 +01:00
}
2020-06-18 20:05:37 +02:00
/*@internal*/
Issue "Cannot find name did-you-mean" errors as suggestions in plain JS (#44271)
* Always issue cannot find name did-you-mean error
This PR issues "cannot find ${name}, did you mean ${name}" errors for
identifiers and propery access expressions in JS files *without*
`// @ts-check` and without `// @ts-nocheck`. This brings some benefits of
Typescript's binder to all Javascript users, even those who haven't
opted into Typescript checking.
```js
export var inModule = 1
inmodule.toFixed() // errors on exports
function f() {
var locals = 2
locale.toFixed() // errors on locals
}
var object = {
spaaace: 3
}
object.spaaaace // error on read
object.spaace = 2 // error on write
object.fresh = 12 // OK, no spelling correction to offer
```
To disable the errors, add `// @ts-nocheck` to the file. To get the
normal checkJs experience, add `// @ts-check`.
== Why This Works ==
In a word: precision. This change has low recall — it misses lots
of correct errors that would be nice to show — but it has high
precision: almost all the errors it shows are correct. And they come
with a suggested correction.
Here are the ingredients:
1. For unchecked JS files, the compiler suppresses all errors except
two did-you-mean name resolution errors.
2. Did-you-mean spelling correction is already tuned for high
precision/low recall, and doesn't show many bogus errors even in JS.
3. For identifiers, the error is suppressed for suggestions from global files.
These are often DOM feature detection, for example.
4. For property accesses, the error is suppressed for suggestions from
other files, for the same reason.
5. For property accesses, the error is suppressed for `this` property
accesses because the compiler doesn't understand JS constructor
functions well enough.
In particular, it doesn't understand any inheritance patterns.
== Work Remaining ==
1. Code cleanup.
2. Fix a couple of failures in existing tests.
3. Suppress errors on property access suggestions from large objects.
4. Combine (3) and (4) above to suppress errors on suggestions from other, global files.
5. A little more testing on random files to make sure that precision
is good there too.
6. Have people from the regular Code editor meeting test the code and
suggest ideas.
* all (most?) tests pass
* NOW they all pass
* add tonnes of semi-colons
* restore this.x check+add a test case
* make ts-ignore/no-check codefix work in unchecked js
* Issues errors only in the language service
* add a few more tests
* fix incorrect parentheses
* More cleanup in program.ts
* Improve readability of isExcludedJSError
* make diff in program.ts smaller via closure
* Switch unchecked JS did-you-mean to suggestion
Instead of selectively letting errors through.
* undo more missed changes
* disallow ignoring suggestions
* Issue different messages for plain JS than others
Straw text for the messages, I just changed the modals to avoid name
collisions.
2021-06-15 17:54:08 +02:00
export function filterSemanticDiagnostics ( diagnostic : readonly Diagnostic [ ] , option : CompilerOptions ) : readonly Diagnostic [ ] {
2020-06-18 20:05:37 +02:00
return filter ( diagnostic , d = > ! d . skippedOn || ! option [ d . skippedOn ] ) ;
}
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 ;
2019-08-08 20:30:18 +02:00
readDirectory ? ( rootDir : string , extensions : readonly string [ ] , excludes : readonly string [ ] | undefined , includes : readonly string [ ] , depth? : number ) : string [ ] ;
2018-12-19 01:12:37 +01:00
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 ) {
2020-02-25 03:20:58 +01:00
Debug . assertIsDefined ( 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 */
2019-08-08 20:30:18 +02:00
export function createPrependNodes ( projectReferences : readonly ProjectReference [ ] | undefined , getCommandLine : ( ref : ProjectReference , index : number ) = > ParsedCommandLine | undefined , readFile : ( path : string ) = > string | undefined ) {
2019-02-01 23:46:58 +01:00
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 ) {
2020-06-02 20:49:21 +02:00
const out = outFile ( resolvedRefOpts . options ) ;
2019-02-01 23:46:58 +01:00
// 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
}
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() {
2020-09-01 19:16:08 +02:00
return getAllowJSCompilerOption ( options ) || ! 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
2020-10-31 00:16:23 +01:00
function getModuleNames ( { imports , module Augmentations } : SourceFile ) : string [ ] {
2017-08-26 00:14:51 +02:00
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 ;
}
2020-12-09 01:10:05 +01:00
2021-01-09 00:20:29 +01:00
/* @internal */
2021-09-24 23:25:59 +02:00
export function getModuleNameStringLiteralAt ( { imports , module Augmentations } : SourceFileImportsList , index : number ) : StringLiteralLike {
2020-12-09 01:10:05 +01:00
if ( index < imports . length ) return imports [ index ] ;
let augIndex = imports . length ;
for ( const aug of module Augmentations ) {
if ( aug . kind === SyntaxKind . StringLiteral ) {
if ( index === augIndex ) return aug ;
augIndex ++ ;
}
// Do nothing if it's an Identifier; we don't need to do module resolution for `declare global`.
}
Debug . fail ( "should never ask for module name at index higher than possible module name" ) ;
}
2015-06-17 20:08:13 +02:00
}