2017-03-23 00:23:21 +01:00
/// <reference path="sys.ts" />
2014-12-16 22:14:14 +01:00
/// <reference path="emitter.ts" />
2015-07-29 04:26:18 +02:00
/// <reference path="core.ts" />
2014-12-16 22:14:14 +01:00
2015-06-12 18:01:48 +02:00
namespace ts {
2015-11-04 23:02:33 +01:00
const emptyArray : any [ ] = [ ] ;
2017-03-23 00:23:21 +01:00
const ignoreDiagnosticCommentRegEx = /(^\s*$)|(^\s*\/\/\/?\s*(@ts-ignore)?)/ ;
2015-10-01 01:10:52 +02:00
2016-08-20 01:49:55 +02:00
export function findConfigFile ( searchPath : string , fileExists : ( fileName : string ) = > boolean , configName = "tsconfig.json" ) : string {
2015-03-24 22:03:21 +01:00
while ( true ) {
2016-06-24 22:56:45 +02:00
const fileName = combinePaths ( searchPath , configName ) ;
2015-10-05 21:27:06 +02:00
if ( fileExists ( fileName ) ) {
2015-03-24 22:03:21 +01:00
return fileName ;
}
2015-11-04 23:02:33 +01:00
const parentPath = getDirectoryPath ( searchPath ) ;
2015-03-24 22:03:21 +01:00
if ( parentPath === searchPath ) {
break ;
}
searchPath = parentPath ;
}
return undefined ;
}
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 */
export function computeCommonSourceDirectoryOfFilenames ( fileNames : string [ ] , currentDirectory : string , getCanonicalFileName : ( fileName : string ) = > string ) : string {
2016-04-01 21:41:01 +02:00
let commonPathComponents : string [ ] ;
const failed = forEach ( fileNames , sourceFile = > {
// Each file contributes into common source file path
const sourcePathComponents = getNormalizedPathComponents ( sourceFile , currentDirectory ) ;
sourcePathComponents . pop ( ) ; // The base file name is not part of the common directory path
if ( ! commonPathComponents ) {
// first file
commonPathComponents = sourcePathComponents ;
return ;
}
2016-12-18 07:44:54 +01:00
const n = Math . min ( commonPathComponents . length , sourcePathComponents . length ) ;
for ( let i = 0 ; i < n ; i ++ ) {
2016-04-01 21:41:01 +02:00
if ( getCanonicalFileName ( commonPathComponents [ i ] ) !== getCanonicalFileName ( sourcePathComponents [ i ] ) ) {
if ( i === 0 ) {
// Failed to find any common path component
return true ;
}
// New common path found that is 0 -> i-1
commonPathComponents . length = i ;
break ;
}
}
// If the sourcePathComponents was shorter than the commonPathComponents, truncate to the sourcePathComponents
if ( sourcePathComponents . length < commonPathComponents . length ) {
commonPathComponents . length = sourcePathComponents . length ;
}
} ) ;
// A common path can not be found when paths span multiple drives on windows, for example
if ( failed ) {
return "" ;
}
if ( ! commonPathComponents ) { // Can happen when all input files are .d.ts files
return currentDirectory ;
}
return getNormalizedPathFromPathComponents ( commonPathComponents ) ;
}
2016-02-09 15:23:43 +01:00
interface OutputFingerprint {
hash : string ;
byteOrderMark : boolean ;
mtime : Date ;
}
2015-03-18 22:11:50 +01:00
export function createCompilerHost ( options : CompilerOptions , setParentNodes? : boolean ) : CompilerHost {
2016-10-28 00:50:21 +02:00
const existingDirectories = createMap < boolean > ( ) ;
2014-12-16 22:14:14 +01:00
function getCanonicalFileName ( fileName : string ) : string {
// if underlying system can distinguish between two files whose names differs only in cases then file name already in canonical form.
// otherwise use toLowerCase as a canonical form.
return sys . useCaseSensitiveFileNames ? fileName : fileName.toLowerCase ( ) ;
}
2015-06-26 01:24:41 +02:00
2015-02-04 01:08:46 +01:00
function getSourceFile ( fileName : string , languageVersion : ScriptTarget , onError ? : ( message : string ) = > void ) : SourceFile {
2015-03-13 23:03:17 +01:00
let text : string ;
2014-12-16 22:14:14 +01:00
try {
2016-08-15 20:07:49 +02:00
performance . mark ( "beforeIORead" ) ;
2015-03-13 23:03:17 +01:00
text = sys . readFile ( fileName , options . charset ) ;
2016-08-15 20:07:49 +02:00
performance . mark ( "afterIORead" ) ;
performance . measure ( "I/O Read" , "beforeIORead" , "afterIORead" ) ;
2014-12-16 22:14:14 +01:00
}
catch ( e ) {
if ( onError ) {
2017-03-10 16:10:58 +01:00
onError ( e . message ) ;
2014-12-16 22:14:14 +01:00
}
text = "" ;
}
2015-04-12 23:02:58 +02:00
2015-03-18 22:11:50 +01:00
return text !== undefined ? createSourceFile ( fileName , text , languageVersion , setParentNodes ) : undefined ;
2014-12-16 22:14:14 +01:00
}
2015-03-13 22:49:32 +01:00
function directoryExists ( directoryPath : string ) : boolean {
2016-12-05 23:13:32 +01:00
if ( existingDirectories . has ( directoryPath ) ) {
2015-03-13 22:49:32 +01:00
return true ;
2014-12-16 22:14:14 +01:00
}
2015-03-13 22:49:32 +01:00
if ( sys . directoryExists ( directoryPath ) ) {
2016-12-05 23:13:32 +01:00
existingDirectories . set ( directoryPath , true ) ;
2015-03-13 22:49:32 +01:00
return true ;
2014-12-16 22:14:14 +01:00
}
2015-03-13 22:49:32 +01:00
return false ;
}
2014-12-16 22:14:14 +01:00
2015-03-13 22:49:32 +01:00
function ensureDirectoriesExist ( directoryPath : string ) {
if ( directoryPath . length > getRootLength ( directoryPath ) && ! directoryExists ( directoryPath ) ) {
2015-11-04 23:02:33 +01:00
const parentDirectory = getDirectoryPath ( directoryPath ) ;
2015-03-13 22:49:32 +01:00
ensureDirectoriesExist ( parentDirectory ) ;
sys . createDirectory ( directoryPath ) ;
2014-12-16 22:14:14 +01:00
}
2015-03-13 22:49:32 +01:00
}
2014-12-16 22:14:14 +01:00
2016-10-28 00:50:21 +02:00
let outputFingerprints : Map < OutputFingerprint > ;
2016-02-11 09:38:21 +01:00
function writeFileIfUpdated ( fileName : string , data : string , writeByteOrderMark : boolean ) : void {
if ( ! outputFingerprints ) {
2016-10-28 00:50:21 +02:00
outputFingerprints = createMap < OutputFingerprint > ( ) ;
2016-02-11 09:38:21 +01:00
}
const hash = sys . createHash ( data ) ;
const mtimeBefore = sys . getModifiedTime ( fileName ) ;
2016-12-05 23:13:32 +01:00
if ( mtimeBefore ) {
const fingerprint = outputFingerprints . get ( fileName ) ;
2016-02-11 09:38:21 +01:00
// If output has not been changed, and the file has no external modification
2016-12-05 23:13:32 +01:00
if ( fingerprint &&
fingerprint . byteOrderMark === writeByteOrderMark &&
2016-02-11 09:38:21 +01:00
fingerprint . hash === hash &&
fingerprint . mtime . getTime ( ) === mtimeBefore . getTime ( ) ) {
return ;
2016-02-09 15:23:43 +01:00
}
2016-02-11 09:38:21 +01:00
}
2016-02-09 15:23:43 +01:00
2016-02-11 09:38:21 +01:00
sys . writeFile ( fileName , data , writeByteOrderMark ) ;
2016-02-09 15:23:43 +01:00
2016-02-11 09:38:21 +01:00
const mtimeAfter = sys . getModifiedTime ( fileName ) ;
2016-02-09 15:23:43 +01:00
2016-12-05 23:13:32 +01:00
outputFingerprints . set ( fileName , {
2016-02-11 09:38:21 +01:00
hash ,
byteOrderMark : writeByteOrderMark ,
mtime : mtimeAfter
2016-12-05 23:13:32 +01:00
} ) ;
2016-02-11 09:38:21 +01:00
}
2016-02-09 15:23:43 +01:00
2015-03-13 22:49:32 +01:00
function writeFile ( fileName : string , data : string , writeByteOrderMark : boolean , onError ? : ( message : string ) = > void ) {
2014-12-16 22:14:14 +01:00
try {
2016-08-15 20:07:49 +02:00
performance . mark ( "beforeIOWrite" ) ;
2014-12-16 22:14:14 +01:00
ensureDirectoriesExist ( getDirectoryPath ( normalizePath ( fileName ) ) ) ;
2016-02-11 09:38:21 +01:00
2016-04-04 23:51:16 +02:00
if ( isWatchSet ( options ) && sys . createHash && sys . getModifiedTime ) {
2016-02-11 09:38:21 +01:00
writeFileIfUpdated ( fileName , data , writeByteOrderMark ) ;
}
else {
sys . writeFile ( fileName , data , writeByteOrderMark ) ;
}
2016-08-15 20:07:49 +02:00
performance . mark ( "afterIOWrite" ) ;
performance . measure ( "I/O Write" , "beforeIOWrite" , "afterIOWrite" ) ;
2014-12-16 22:14:14 +01:00
}
catch ( e ) {
if ( onError ) {
onError ( e . message ) ;
}
}
}
2016-03-29 01:24:16 +02:00
function getDefaultLibLocation ( ) : string {
return getDirectoryPath ( normalizePath ( sys . getExecutingFilePath ( ) ) ) ;
2016-03-28 23:20:29 +02:00
}
2015-05-27 05:18:13 +02:00
const newLine = getNewLineCharacter ( options ) ;
2016-05-05 22:38:09 +02:00
const realpath = sys . realpath && ( ( path : string ) = > sys . realpath ( path ) ) ;
2015-10-01 01:10:52 +02:00
2014-12-16 22:14:14 +01:00
return {
getSourceFile ,
2016-03-29 01:24:16 +02:00
getDefaultLibLocation ,
2016-03-30 19:26:39 +02:00
getDefaultLibFileName : options = > combinePaths ( getDefaultLibLocation ( ) , getDefaultLibFileName ( options ) ) ,
2014-12-16 22:14:14 +01:00
writeFile ,
2015-10-15 23:43:51 +02:00
getCurrentDirectory : memoize ( ( ) = > sys . getCurrentDirectory ( ) ) ,
2014-12-16 22:14:14 +01:00
useCaseSensitiveFileNames : ( ) = > sys . useCaseSensitiveFileNames ,
getCanonicalFileName ,
2015-07-14 02:44:50 +02:00
getNewLine : ( ) = > newLine ,
2015-08-05 06:22:37 +02:00
fileExists : fileName = > sys . fileExists ( fileName ) ,
2016-01-06 21:37:52 +01:00
readFile : fileName = > sys . readFile ( fileName ) ,
2016-01-06 23:16:56 +01:00
trace : ( s : string ) = > sys . write ( s + newLine ) ,
2016-05-05 22:38:09 +02:00
directoryExists : directoryName = > sys . directoryExists ( directoryName ) ,
2016-10-04 23:00:45 +02:00
getEnvironmentVariable : name = > sys . getEnvironmentVariable ? sys . getEnvironmentVariable ( name ) : "" ,
2016-06-11 00:44:11 +02:00
getDirectories : ( path : string ) = > sys . getDirectories ( path ) ,
2016-05-05 22:38:09 +02:00
realpath
2014-12-16 22:14:14 +01:00
} ;
}
2015-06-18 21:04:26 +02:00
export function getPreEmitDiagnostics ( program : Program , sourceFile? : SourceFile , cancellationToken? : CancellationToken ) : Diagnostic [ ] {
2015-11-06 21:39:42 +01:00
let diagnostics = program . getOptionsDiagnostics ( cancellationToken ) . concat (
2016-02-23 21:48:31 +01:00
program . getSyntacticDiagnostics ( sourceFile , cancellationToken ) ,
program . getGlobalDiagnostics ( cancellationToken ) ,
program . getSemanticDiagnostics ( sourceFile , cancellationToken ) ) ;
2015-03-20 00:55:07 +01:00
if ( program . getCompilerOptions ( ) . declaration ) {
2015-10-21 00:51:37 +02:00
diagnostics = diagnostics . concat ( program . getDeclarationDiagnostics ( sourceFile , cancellationToken ) ) ;
2015-03-20 00:55:07 +01:00
}
2015-02-05 10:47:29 +01:00
return sortAndDeduplicateDiagnostics ( diagnostics ) ;
}
2016-07-15 08:02:56 +02:00
export interface FormatDiagnosticsHost {
getCurrentDirectory ( ) : string ;
getCanonicalFileName ( fileName : string ) : string ;
getNewLine ( ) : string ;
}
export function formatDiagnostics ( diagnostics : Diagnostic [ ] , host : FormatDiagnosticsHost ) : string {
2016-07-13 18:13:55 +02:00
let output = "" ;
for ( const diagnostic of diagnostics ) {
if ( diagnostic . file ) {
const { line , character } = getLineAndCharacterOfPosition ( diagnostic . file , diagnostic . start ) ;
const fileName = diagnostic . file . fileName ;
const relativeFileName = convertToRelativePath ( fileName , host . getCurrentDirectory ( ) , fileName = > host . getCanonicalFileName ( fileName ) ) ;
2016-11-04 05:13:41 +01:00
output += ` ${ relativeFileName } ( ${ line + 1 } , ${ character + 1 } ): ` ;
2016-07-13 18:13:55 +02:00
}
const category = DiagnosticCategory [ diagnostic . category ] . toLowerCase ( ) ;
2016-11-04 05:13:41 +01:00
output += ` ${ category } TS ${ diagnostic . code } : ${ flattenDiagnosticMessageText ( diagnostic . messageText , host . getNewLine ( ) ) } ${ host . getNewLine ( ) } ` ;
2016-07-13 18:13:55 +02:00
}
return output ;
}
2015-02-05 10:47:29 +01:00
export function flattenDiagnosticMessageText ( messageText : string | DiagnosticMessageChain , newLine : string ) : string {
if ( typeof messageText === "string" ) {
return messageText ;
}
else {
2015-03-13 23:03:17 +01:00
let diagnosticChain = messageText ;
let result = "" ;
2015-02-05 10:47:29 +01:00
2015-03-13 23:03:17 +01:00
let indent = 0 ;
2015-02-05 10:47:29 +01:00
while ( diagnosticChain ) {
if ( indent ) {
result += newLine ;
2015-03-13 23:03:17 +01:00
for ( let i = 0 ; i < indent ; i ++ ) {
2015-02-05 10:47:29 +01:00
result += " " ;
}
}
result += diagnosticChain . messageText ;
indent ++ ;
diagnosticChain = diagnosticChain . next ;
}
return result ;
}
}
2016-04-01 21:41:01 +02:00
function loadWithLocalCache < T > ( names : string [ ] , containingFile : string , loader : ( name : string , containingFile : string ) = > T ) : T [ ] {
if ( names . length === 0 ) {
return [ ] ;
}
const resolutions : T [ ] = [ ] ;
2016-10-28 00:50:21 +02:00
const cache = createMap < T > ( ) ;
2016-04-01 21:41:01 +02:00
for ( const name of names ) {
2016-12-12 17:42:12 +01:00
let result : T ;
if ( cache . has ( name ) ) {
result = cache . get ( name ) ;
}
else {
cache . set ( name , result = loader ( name , containingFile ) ) ;
}
2016-04-01 21:41:01 +02:00
resolutions . push ( result ) ;
}
return resolutions ;
}
2017-03-07 22:26:41 +01:00
interface DiagnosticCache {
perFile? : FileMap < Diagnostic [ ] > ;
allDiagnostics? : Diagnostic [ ] ;
}
2015-06-23 02:48:44 +02:00
export function createProgram ( rootNames : string [ ] , options : CompilerOptions , host? : CompilerHost , oldProgram? : Program ) : Program {
2015-03-13 23:03:17 +01:00
let program : Program ;
let files : SourceFile [ ] = [ ] ;
2015-11-18 23:10:53 +01:00
let commonSourceDirectory : string ;
2015-03-17 14:26:24 +01:00
let diagnosticsProducingTypeChecker : TypeChecker ;
let noDiagnosticsTypeChecker : TypeChecker ;
2016-10-28 00:50:21 +02:00
let classifiableNames : Map < string > ;
2014-12-16 22:14:14 +01:00
2017-03-17 21:37:12 +01:00
const cachedSemanticDiagnosticsForFile : DiagnosticCache = { } ;
const cachedDeclarationDiagnosticsForFile : DiagnosticCache = { } ;
2017-03-07 22:26:41 +01:00
2016-10-28 00:50:21 +02:00
let resolvedTypeReferenceDirectives = createMap < ResolvedTypeReferenceDirective > ( ) ;
2016-04-01 21:41:01 +02:00
let fileProcessingDiagnostics = createDiagnosticCollection ( ) ;
2015-05-01 03:14:53 +02:00
2016-06-21 22:42:02 +02:00
// The below settings are to track if a .js file should be add to the program if loaded via searching under node_modules.
// This works as imported modules are discovered recursively in a depth first manner, specifically:
// - For each root file, findSourceFile is called.
// - This calls processImportedModules for each module imported in the source file.
// - This calls resolveModuleNames, and then calls findSourceFile for each resolved module.
// As all these operations happen - and are nested - within the createProgram call, they close over the below variables.
// The current resolution depth is tracked by incrementing/decrementing as the depth first search progresses.
2016-10-13 22:02:22 +02:00
const maxNodeModuleJsDepth = typeof options . maxNodeModuleJsDepth === "number" ? options.maxNodeModuleJsDepth : 0 ;
2016-07-11 01:11:42 +02:00
let currentNodeModulesDepth = 0 ;
2016-06-21 22:42:02 +02:00
2016-06-27 05:48:22 +02:00
// If a module has some of its imports skipped due to being at the depth limit under node_modules, then track
// this, as it may be imported at a shallower depth later, and then it will need its skipped imports processed.
2016-10-28 00:50:21 +02:00
const module sWithElidedImports = createMap < boolean > ( ) ;
2016-06-27 05:48:22 +02:00
2016-07-11 01:11:42 +02:00
// Track source files that are source files found by searching under node_modules, as these shouldn't be compiled.
2016-10-28 00:50:21 +02:00
const sourceFilesFoundSearchingNodeModules = createMap < boolean > ( ) ;
2016-06-27 05:48:22 +02:00
2016-08-15 20:07:49 +02:00
performance . mark ( "beforeProgram" ) ;
2014-12-16 22:14:14 +01:00
2015-03-17 14:26:24 +01:00
host = host || createCompilerHost ( options ) ;
2016-04-01 21:41:01 +02:00
2016-04-11 05:42:22 +02:00
let skipDefaultLib = options . noLib ;
const programDiagnostics = createDiagnosticCollection ( ) ;
const currentDirectory = host . getCurrentDirectory ( ) ;
const supportedExtensions = getSupportedExtensions ( options ) ;
2015-10-30 23:54:31 +01:00
// Map storing if there is emit blocking diagnostics for given input
2015-11-18 19:48:03 +01:00
const hasEmitBlockingDiagnostics = createFileMap < boolean > ( getCanonicalFileName ) ;
2015-10-01 01:10:52 +02:00
2016-12-19 22:48:45 +01:00
let module ResolutionCache : ModuleResolutionCache ;
2016-10-27 20:33:01 +02:00
let resolveModuleNamesWorker : ( module Names : string [ ] , containingFile : string ) = > ResolvedModuleFull [ ] ;
2016-04-01 21:41:01 +02:00
if ( host . resolveModuleNames ) {
2016-10-25 21:38:59 +02:00
resolveModuleNamesWorker = ( module Names , containingFile ) = > host . resolveModuleNames ( module Names , containingFile ) . map ( resolved = > {
// An older host may have omitted extension, in which case we should infer it from the file extension of resolvedFileName.
2016-10-27 20:33:01 +02:00
if ( ! resolved || ( resolved as ResolvedModuleFull ) . extension !== undefined ) {
return resolved as ResolvedModuleFull ;
2016-10-25 21:38:59 +02:00
}
2016-10-27 20:33:01 +02:00
const withExtension = clone ( resolved ) as ResolvedModuleFull ;
withExtension . extension = extensionFromPath ( resolved . resolvedFileName ) ;
return withExtension ;
2016-10-25 21:38:59 +02:00
} ) ;
2016-04-01 21:41:01 +02:00
}
else {
2016-12-19 22:48:45 +01:00
module ResolutionCache = createModuleResolutionCache ( currentDirectory , x = > host . getCanonicalFileName ( x ) ) ;
const loader = ( module Name : string , containingFile : string ) = > resolveModuleName ( module Name , containingFile , options , host , module ResolutionCache ) . resolvedModule ;
2016-04-01 21:41:01 +02:00
resolveModuleNamesWorker = ( module Names , containingFile ) = > loadWithLocalCache ( module Names , containingFile , loader ) ;
}
let resolveTypeReferenceDirectiveNamesWorker : ( typeDirectiveNames : string [ ] , containingFile : string ) = > ResolvedTypeReferenceDirective [ ] ;
if ( host . resolveTypeReferenceDirectives ) {
resolveTypeReferenceDirectiveNamesWorker = ( typeDirectiveNames , containingFile ) = > host . resolveTypeReferenceDirectives ( typeDirectiveNames , containingFile ) ;
}
else {
2016-04-06 01:33:11 +02:00
const loader = ( typesRef : string , containingFile : string ) = > resolveTypeReferenceDirective ( typesRef , containingFile , options , host ) . resolvedTypeReferenceDirective ;
2016-04-01 21:41:01 +02:00
resolveTypeReferenceDirectiveNamesWorker = ( typeReferenceDirectiveNames , containingFile ) = > loadWithLocalCache ( typeReferenceDirectiveNames , containingFile , loader ) ;
}
2015-05-01 03:14:53 +02:00
2015-11-04 23:02:33 +01:00
const filesByName = createFileMap < SourceFile > ( ) ;
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
2015-11-04 23:02:33 +01:00
const filesByNameIgnoreCase = host . useCaseSensitiveFileNames ( ) ? createFileMap < SourceFile > ( fileName = > fileName . toLowerCase ( ) ) : undefined ;
2015-10-01 01:10:52 +02:00
2016-04-06 22:49:25 +02:00
if ( ! tryReuseStructureFromOldProgram ( ) ) {
2016-05-18 20:30:40 +02:00
forEach ( rootNames , name = > processRootFile ( name , /*isDefaultLib*/ false ) ) ;
2016-06-11 00:44:11 +02:00
// load type declarations specified via 'types' argument or implicitly from types/ and node_modules/@types folders
2016-08-04 16:43:54 +02:00
const typeReferences : string [ ] = getAutomaticTypeDirectiveNames ( options , host ) ;
2016-05-18 00:41:31 +02:00
2016-10-07 17:31:53 +02:00
if ( typeReferences . length ) {
2016-08-04 16:43:54 +02:00
// This containingFilename needs to match with the one used in managed-side
2016-11-18 22:39:11 +01:00
const containingDirectory = options . configFilePath ? getDirectoryPath ( options . configFilePath ) : host . getCurrentDirectory ( ) ;
const containingFilename = combinePaths ( containingDirectory , "__inferred type names__.ts" ) ;
2016-06-11 00:44:11 +02:00
const resolutions = resolveTypeReferenceDirectiveNamesWorker ( typeReferences , containingFilename ) ;
2016-05-18 00:41:31 +02:00
for ( let i = 0 ; i < typeReferences . length ; i ++ ) {
processTypeReferenceDirective ( typeReferences [ i ] , resolutions [ i ] ) ;
2016-04-06 22:49:25 +02:00
}
2015-06-25 02:40:04 +02:00
}
2015-10-01 01:10:52 +02:00
2015-06-23 02:48:44 +02:00
// Do not process the default library if:
// - The '--noLib' flag is used.
// - A 'no-default-lib' reference comment is encountered in
// processing the root files.
if ( ! skipDefaultLib ) {
2016-03-28 23:20:29 +02:00
// If '--lib' is not specified, include default library file according to '--target'
// otherwise, using options specified in '--lib' instead of '--target' default library file
if ( ! options . lib ) {
processRootFile ( host . getDefaultLibFileName ( options ) , /*isDefaultLib*/ true ) ;
}
else {
2016-03-29 01:24:16 +02:00
const libDirectory = host . getDefaultLibLocation ? host . getDefaultLibLocation ( ) : getDirectoryPath ( host . getDefaultLibFileName ( options ) ) ;
forEach ( options . lib , libFileName = > {
processRootFile ( combinePaths ( libDirectory , libFileName ) , /*isDefaultLib*/ true ) ;
2016-03-28 23:20:29 +02:00
} ) ;
}
2015-06-23 02:48:44 +02:00
}
2014-12-16 22:14:14 +01:00
}
2015-05-01 03:14:53 +02:00
2016-12-19 22:48:45 +01:00
// unconditionally set moduleResolutionCache to undefined to avoid unnecessary leaks
module ResolutionCache = undefined ;
2015-07-09 23:45:39 +02:00
// unconditionally set oldProgram to undefined to prevent it from being captured in closure
oldProgram = undefined ;
2014-12-16 22:14:14 +01:00
program = {
2015-06-23 02:48:44 +02:00
getRootFileNames : ( ) = > rootNames ,
2015-10-29 22:54:56 +01:00
getSourceFile ,
2016-05-11 08:43:26 +02:00
getSourceFileByPath ,
2014-12-16 22:14:14 +01:00
getSourceFiles : ( ) = > files ,
getCompilerOptions : ( ) = > options ,
2015-02-05 10:47:29 +01:00
getSyntacticDiagnostics ,
2015-06-18 18:32:52 +02:00
getOptionsDiagnostics ,
2015-02-04 23:29:25 +01:00
getGlobalDiagnostics ,
2015-02-05 10:47:29 +01:00
getSemanticDiagnostics ,
2015-02-04 23:29:25 +01:00
getDeclarationDiagnostics ,
2014-12-16 22:14:14 +01:00
getTypeChecker ,
2015-06-12 21:53:24 +02:00
getClassifiableNames ,
2015-02-05 01:11:38 +01:00
getDiagnosticsProducingTypeChecker ,
2015-11-19 02:10:22 +01:00
getCommonSourceDirectory ,
2015-02-05 01:53:14 +01:00
emit ,
2015-10-15 23:43:51 +02:00
getCurrentDirectory : ( ) = > currentDirectory ,
2015-02-05 01:11:38 +01:00
getNodeCount : ( ) = > getDiagnosticsProducingTypeChecker ( ) . getNodeCount ( ) ,
getIdentifierCount : ( ) = > getDiagnosticsProducingTypeChecker ( ) . getIdentifierCount ( ) ,
getSymbolCount : ( ) = > getDiagnosticsProducingTypeChecker ( ) . getSymbolCount ( ) ,
getTypeCount : ( ) = > getDiagnosticsProducingTypeChecker ( ) . getTypeCount ( ) ,
2016-04-01 21:41:01 +02:00
getFileProcessingDiagnostics : ( ) = > fileProcessingDiagnostics ,
2016-06-18 01:22:03 +02:00
getResolvedTypeReferenceDirectives : ( ) = > resolvedTypeReferenceDirectives ,
2016-11-08 06:13:11 +01:00
isSourceFileFromExternalLibrary ,
2016-06-18 01:22:03 +02:00
dropDiagnosticsProducingTypeChecker
2014-12-16 22:14:14 +01:00
} ;
2015-10-12 21:25:13 +02:00
verifyCompilerOptions ( ) ;
2016-08-15 20:07:49 +02:00
performance . mark ( "afterProgram" ) ;
performance . measure ( "Program" , "beforeProgram" , "afterProgram" ) ;
2015-10-12 22:10:54 +02:00
2014-12-16 22:14:14 +01:00
return program ;
2015-11-19 02:10:22 +01:00
function getCommonSourceDirectory() {
2016-11-02 15:05:54 +01:00
if ( commonSourceDirectory === undefined ) {
2017-01-20 17:12:00 +01:00
const emittedFiles = filter ( files , file = > sourceFileMayBeEmitted ( file , options , isSourceFileFromExternalLibrary ) ) ;
2016-11-02 15:05:54 +01:00
if ( options . rootDir && checkSourceFilesBelongToPath ( emittedFiles , options . rootDir ) ) {
2015-11-19 02:10:22 +01:00
// If a rootDir is specified and is valid use it as the commonSourceDirectory
commonSourceDirectory = getNormalizedAbsolutePath ( options . rootDir , currentDirectory ) ;
}
else {
2016-11-02 15:05:54 +01:00
commonSourceDirectory = computeCommonSourceDirectory ( emittedFiles ) ;
2015-11-19 02:10:22 +01:00
}
if ( commonSourceDirectory && commonSourceDirectory [ commonSourceDirectory . length - 1 ] !== directorySeparator ) {
// Make sure directory path ends with directory separator so this string can directly
// used to replace with "" to get the relative path of the source file and the relative path doesn't
// start with / making it rooted path
commonSourceDirectory += directorySeparator ;
}
}
return commonSourceDirectory ;
}
2016-10-28 00:50:21 +02:00
function getClassifiableNames() {
2015-06-12 21:53:24 +02:00
if ( ! classifiableNames ) {
2015-06-11 03:18:37 +02:00
// Initialize a checker so that all our files are bound.
getTypeChecker ( ) ;
2016-10-28 00:50:21 +02:00
classifiableNames = createMap < string > ( ) ;
for ( const sourceFile of files ) {
2016-12-28 18:16:38 +01:00
copyEntries ( sourceFile . classifiableNames , classifiableNames ) ;
2016-10-28 00:50:21 +02:00
}
2015-06-11 03:18:37 +02:00
}
2015-06-12 21:53:24 +02:00
return classifiableNames ;
2015-06-11 03:18:37 +02:00
}
2016-11-02 22:41:26 +01:00
interface OldProgramState {
program : Program ;
file : SourceFile ;
modifiedFilePaths : Path [ ] ;
}
function resolveModuleNamesReusingOldState ( module Names : string [ ] , containingFile : string , file : SourceFile , oldProgramState? : OldProgramState ) {
if ( ! oldProgramState && ! file . ambientModuleNames . length ) {
// if old program state is not supplied and file does not contain locally defined ambient modules
// then the best we can do is fallback to the default logic
return resolveModuleNamesWorker ( module Names , containingFile ) ;
}
2016-11-09 16:41:25 +01:00
// at this point we know that either
2016-11-02 22:41:26 +01:00
// - file has local declarations for ambient modules
// OR
// - old program state is available
// OR
// - both of items above
// With this it is possible that we can tell how some module names from the initial list will be resolved
// without doing actual resolution (in particular if some name was resolved to ambient module).
// Such names should be excluded from the list of module names that will be provided to `resolveModuleNamesWorker`
// since we don't want to resolve them again.
// this is a list of modules for which we cannot predict resolution so they should be actually resolved
let unknownModuleNames : string [ ] ;
// this is a list of combined results assembles from predicted and resolved results.
// Order in this list matches the order in the original list of module names `moduleNames` which is important
// so later we can split results to resolutions of modules and resolutions of module augmentations.
let result : ResolvedModuleFull [ ] ;
// a transient placeholder that is used to mark predicted resolution in the result list
const predictedToResolveToAmbientModuleMarker : ResolvedModuleFull = < any > { } ;
for ( let i = 0 ; i < module Names.length ; i ++ ) {
const module Name = module Names [ i ] ;
// module name is known to be resolved to ambient module if
// - module name is contained in the list of ambient modules that are locally declared in the file
// - in the old program module name was resolved to ambient module whose declaration is in non-modified file
// (so the same module declaration will land in the new program)
let isKnownToResolveToAmbientModule = false ;
if ( contains ( file . ambientModuleNames , module Name ) ) {
isKnownToResolveToAmbientModule = true ;
if ( isTraceEnabled ( options , host ) ) {
trace ( host , Diagnostics . Module_0_was_resolved_as_locally_declared_ambient_module_in_file_1 , module Name , containingFile ) ;
}
}
else {
isKnownToResolveToAmbientModule = checkModuleNameResolvedToAmbientModuleInNonModifiedFile ( module Name , oldProgramState ) ;
}
if ( isKnownToResolveToAmbientModule ) {
if ( ! unknownModuleNames ) {
// found a first module name for which result can be prediced
// this means that this module name should not be passed to `resolveModuleNamesWorker`.
// We'll use a separate list for module names that are definitely unknown.
result = new Array ( module Names.length ) ;
// copy all module names that appear before the current one in the list
// since they are known to be unknown
unknownModuleNames = module Names.slice ( 0 , i ) ;
}
// mark prediced resolution in the result list
result [ i ] = predictedToResolveToAmbientModuleMarker ;
}
else if ( unknownModuleNames ) {
// found unknown module name and we are already using separate list for those - add it to the list
unknownModuleNames . push ( module Name ) ;
}
}
if ( ! unknownModuleNames ) {
// we've looked throught the list but have not seen any predicted resolution
// use default logic
return resolveModuleNamesWorker ( module Names , containingFile ) ;
}
const resolutions = unknownModuleNames . length
? resolveModuleNamesWorker ( unknownModuleNames , containingFile )
: emptyArray ;
// combine results of resolutions and predicted results
let j = 0 ;
for ( let i = 0 ; i < result . length ; i ++ ) {
if ( result [ i ] == predictedToResolveToAmbientModuleMarker ) {
result [ i ] = undefined ;
}
else {
result [ i ] = resolutions [ j ] ;
j ++ ;
}
}
Debug . assert ( j === resolutions . length ) ;
return result ;
function checkModuleNameResolvedToAmbientModuleInNonModifiedFile ( module Name : string , oldProgramState? : OldProgramState ) : boolean {
if ( ! oldProgramState ) {
return false ;
}
const resolutionToFile = getResolvedModule ( oldProgramState . file , module Name ) ;
if ( resolutionToFile ) {
// module used to be resolved to file - ignore it
return false ;
}
const ambientModule = oldProgram . getTypeChecker ( ) . tryFindAmbientModuleWithoutAugmentations ( module Name ) ;
if ( ! ( ambientModule && ambientModule . declarations ) ) {
return false ;
}
// at least one of declarations should come from non-modified source file
const firstUnmodifiedFile = forEach ( ambientModule . declarations , d = > {
const f = getSourceFileOfNode ( d ) ;
return ! contains ( oldProgramState . modifiedFilePaths , f . path ) && f ;
} ) ;
if ( ! firstUnmodifiedFile ) {
return false ;
}
if ( isTraceEnabled ( options , host ) ) {
trace ( host , Diagnostics . Module_0_was_resolved_as_ambient_module_declared_in_1_since_this_file_was_not_modified , module Name , firstUnmodifiedFile . fileName ) ;
}
return true ;
}
}
2015-06-23 02:48:44 +02:00
function tryReuseStructureFromOldProgram ( ) : boolean {
if ( ! oldProgram ) {
return false ;
}
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 ) ) {
2016-04-06 22:49:25 +02:00
return false ;
}
2015-06-24 06:06:57 +02:00
Debug . assert ( ! oldProgram . structureIsReused ) ;
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 ) ) {
2015-06-23 02:48:44 +02:00
return false ;
}
2015-10-01 01:10:52 +02:00
2016-04-06 22:49:25 +02:00
if ( ! arrayIsEqualTo ( options . types , oldOptions . types ) ) {
return false ;
}
2015-06-23 02:48:44 +02:00
// check if program source files has changed in the way that can affect structure of the program
2015-11-04 23:02:33 +01:00
const newSourceFiles : SourceFile [ ] = [ ] ;
const filePaths : Path [ ] = [ ] ;
2016-11-02 22:41:26 +01:00
const modifiedSourceFiles : { oldFile : SourceFile , newFile : SourceFile } [ ] = [ ] ;
2015-10-15 23:43:51 +02:00
2015-11-04 23:02:33 +01:00
for ( const oldSourceFile of oldProgram . getSourceFiles ( ) ) {
2016-05-11 08:43:26 +02:00
let newSourceFile = host . getSourceFileByPath
? host . getSourceFileByPath ( oldSourceFile . fileName , oldSourceFile . path , options . target )
: host . getSourceFile ( oldSourceFile . fileName , options . target ) ;
2015-06-25 02:40:04 +02:00
if ( ! newSourceFile ) {
return false ;
}
2015-10-29 22:54:56 +01:00
newSourceFile . path = oldSourceFile . path ;
filePaths . push ( newSourceFile . path ) ;
2015-10-15 23:43:51 +02:00
2015-10-01 01:10:52 +02:00
if ( oldSourceFile !== newSourceFile ) {
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
return false ;
}
2015-06-23 02:48:44 +02:00
// check tripleslash references
if ( ! arrayIsEqualTo ( oldSourceFile . referencedFiles , newSourceFile . referencedFiles , fileReferenceIsEqualTo ) ) {
// tripleslash references has changed
return false ;
}
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
return false ;
}
2015-12-22 22:21:51 +01:00
if ( ! arrayIsEqualTo ( oldSourceFile . module Augmentations , newSourceFile . module Augmentations , module NameIsEqualTo ) ) {
// moduleAugmentations has changed
return false ;
}
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
return false ;
}
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
}
else {
// file has no changes - use it as is
newSourceFile = oldSourceFile ;
}
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
2016-11-02 22:41:26 +01:00
const modifiedFilePaths = modifiedSourceFiles . map ( f = > f . newFile . path ) ;
2016-11-09 16:41:25 +01:00
// try to verify results of module resolution
2016-11-02 22:41:26 +01:00
for ( const { oldFile : oldSourceFile , newFile : newSourceFile } of modifiedSourceFiles ) {
const newSourceFilePath = getNormalizedAbsolutePath ( newSourceFile . fileName , currentDirectory ) ;
if ( resolveModuleNamesWorker ) {
const module Names = map ( concatenate ( newSourceFile . imports , newSourceFile . module Augmentations ) , getTextOfLiteral ) ;
const resolutions = resolveModuleNamesReusingOldState ( module Names , newSourceFilePath , newSourceFile , { file : oldSourceFile , program : oldProgram , modifiedFilePaths } ) ;
// ensure that module resolution results are still correct
const resolutionsChanged = hasChangesInResolutions ( module Names , resolutions , oldSourceFile . resolvedModules , module ResolutionIsEqualTo ) ;
if ( resolutionsChanged ) {
return false ;
}
}
if ( resolveTypeReferenceDirectiveNamesWorker ) {
const typesReferenceDirectives = map ( newSourceFile . typeReferenceDirectives , x = > x . fileName ) ;
const resolutions = resolveTypeReferenceDirectiveNamesWorker ( typesReferenceDirectives , newSourceFilePath ) ;
// ensure that types resolutions are still correct
const resolutionsChanged = hasChangesInResolutions ( typesReferenceDirectives , resolutions , oldSourceFile . resolvedTypeReferenceDirectiveNames , typeDirectiveIsEqualTo ) ;
if ( resolutionsChanged ) {
return false ;
}
}
// pass the cache of module/types resolutions from the old source file
newSourceFile . resolvedModules = oldSourceFile . resolvedModules ;
newSourceFile . resolvedTypeReferenceDirectiveNames = oldSourceFile . resolvedTypeReferenceDirectiveNames ;
}
2015-06-23 02:48:44 +02:00
// update fileName -> file mapping
2016-12-19 19:12:35 +01:00
for ( let i = 0 ; i < newSourceFiles . length ; i ++ ) {
2015-10-30 00:43:12 +01:00
filesByName . set ( filePaths [ i ] , newSourceFiles [ i ] ) ;
2015-06-23 02:48:44 +02:00
}
2015-10-01 01:10:52 +02:00
2015-06-23 02:48:44 +02:00
files = newSourceFiles ;
2015-09-10 19:46:39 +02:00
fileProcessingDiagnostics = oldProgram . getFileProcessingDiagnostics ( ) ;
2015-10-01 01:10:52 +02:00
2015-11-04 23:02:33 +01:00
for ( const modifiedFile of modifiedSourceFiles ) {
2016-11-02 22:41:26 +01:00
fileProcessingDiagnostics . reattachFileDiagnostics ( modifiedFile . newFile ) ;
2015-09-10 19:46:39 +02:00
}
2016-04-12 06:36:07 +02:00
resolvedTypeReferenceDirectives = oldProgram . getResolvedTypeReferenceDirectives ( ) ;
2015-06-24 06:06:57 +02:00
oldProgram . structureIsReused = true ;
2015-10-01 01:10:52 +02:00
2015-06-23 02:48:44 +02:00
return true ;
}
2015-02-05 10:47:29 +01:00
function getEmitHost ( writeFileCallback? : WriteFileCallback ) : EmitHost {
2015-02-05 01:53:14 +01:00
return {
2015-10-15 23:43:51 +02:00
getCanonicalFileName ,
2015-02-05 01:53:14 +01:00
getCommonSourceDirectory : program.getCommonSourceDirectory ,
getCompilerOptions : program.getCompilerOptions ,
2015-10-15 23:43:51 +02:00
getCurrentDirectory : ( ) = > currentDirectory ,
2015-03-24 18:05:24 +01:00
getNewLine : ( ) = > host . getNewLine ( ) ,
2015-02-05 01:53:14 +01:00
getSourceFile : program.getSourceFile ,
2016-05-11 08:43:26 +02:00
getSourceFileByPath : program.getSourceFileByPath ,
2015-02-05 01:53:14 +01:00
getSourceFiles : program.getSourceFiles ,
2016-11-02 15:05:54 +01:00
isSourceFileFromExternalLibrary ,
2015-03-24 18:05:24 +01:00
writeFile : writeFileCallback || (
2016-03-15 21:25:18 +01:00
( fileName , data , writeByteOrderMark , onError , sourceFiles ) = > host . writeFile ( fileName , data , writeByteOrderMark , onError , sourceFiles ) ) ,
2015-10-20 20:22:46 +02:00
isEmitBlocked ,
2015-02-05 01:53:14 +01:00
} ;
2014-12-16 23:42:58 +01:00
}
2016-11-08 06:13:11 +01:00
function isSourceFileFromExternalLibrary ( file : SourceFile ) : boolean {
2016-12-05 23:13:32 +01:00
return sourceFilesFoundSearchingNodeModules . get ( file . path ) ;
2016-11-08 06:13:11 +01:00
}
2014-12-16 23:12:17 +01:00
function getDiagnosticsProducingTypeChecker() {
return diagnosticsProducingTypeChecker || ( diagnosticsProducingTypeChecker = createTypeChecker ( program , /*produceDiagnostics:*/ true ) ) ;
}
2016-06-18 01:22:03 +02:00
function dropDiagnosticsProducingTypeChecker() {
diagnosticsProducingTypeChecker = undefined ;
}
2015-02-05 01:11:38 +01:00
function getTypeChecker() {
return noDiagnosticsTypeChecker || ( noDiagnosticsTypeChecker = createTypeChecker ( program , /*produceDiagnostics:*/ false ) ) ;
2014-12-16 23:12:17 +01:00
}
2017-02-07 23:36:15 +01:00
function emit ( sourceFile? : SourceFile , writeFileCallback? : WriteFileCallback , cancellationToken? : CancellationToken , emitOnlyDtsFiles? : boolean , transformers? : CustomTransformers ) : EmitResult {
return runWithCancellationToken ( ( ) = > emitWorker ( program , sourceFile , writeFileCallback , cancellationToken , emitOnlyDtsFiles , transformers ) ) ;
2015-06-18 19:52:19 +02:00
}
2015-10-20 20:22:46 +02:00
function isEmitBlocked ( emitFileName : string ) : boolean {
2015-10-30 23:54:31 +01:00
return hasEmitBlockingDiagnostics . contains ( toPath ( emitFileName , currentDirectory , getCanonicalFileName ) ) ;
2015-10-20 20:22:46 +02:00
}
2017-02-07 23:36:15 +01:00
function emitWorker ( program : Program , sourceFile : SourceFile , writeFileCallback : WriteFileCallback , cancellationToken : CancellationToken , emitOnlyDtsFiles? : boolean , customTransformers? : CustomTransformers ) : EmitResult {
2016-02-17 07:01:28 +01:00
let declarationDiagnostics : Diagnostic [ ] = [ ] ;
if ( options . noEmit ) {
2016-04-08 00:29:11 +02:00
return { diagnostics : declarationDiagnostics , sourceMaps : undefined , emittedFiles : undefined , emitSkipped : true } ;
2016-02-17 07:01:28 +01:00
}
2015-02-05 23:41:04 +01:00
// If the noEmitOnError flag is set, then check if we have any errors so far. If so,
2015-07-09 00:35:49 +02:00
// immediately bail out. Note that we pass 'undefined' for 'sourceFile' so that we
// get any preEmit diagnostics, not just the ones
2015-12-22 06:43:51 +01:00
if ( options . noEmitOnError ) {
2016-02-17 07:57:27 +01:00
const diagnostics = program . getOptionsDiagnostics ( cancellationToken ) . concat (
2016-02-17 07:01:28 +01:00
program . getSyntacticDiagnostics ( sourceFile , cancellationToken ) ,
program . getGlobalDiagnostics ( cancellationToken ) ,
program . getSemanticDiagnostics ( sourceFile , cancellationToken ) ) ;
if ( diagnostics . length === 0 && program . getCompilerOptions ( ) . declaration ) {
declarationDiagnostics = program . getDeclarationDiagnostics ( /*sourceFile*/ undefined , cancellationToken ) ;
}
if ( diagnostics . length > 0 || declarationDiagnostics . length > 0 ) {
2016-03-03 02:13:51 +01:00
return {
diagnostics : concatenate ( diagnostics , declarationDiagnostics ) ,
sourceMaps : undefined ,
2016-04-08 00:29:11 +02:00
emittedFiles : undefined ,
2016-03-03 02:13:51 +01:00
emitSkipped : true
} ;
2015-12-22 06:43:51 +01:00
}
2014-12-16 22:52:47 +01:00
}
2015-02-05 23:41:04 +01:00
2015-02-26 01:45:45 +01:00
// Create the emit resolver outside of the "emitTime" tracking code below. That way
// any cost associated with it (like type checking) are appropriate associated with
// the type-checking counter.
2015-05-08 22:58:20 +02:00
//
// If the -out option is specified, we should not pass the source file to getEmitResolver.
// This is because in the -out scenario all files need to be emitted, and therefore all
// files need to be type checked. And the way to specify that all files need to be type
// checked is to not pass the file to getEmitResolver.
2015-11-04 23:02:33 +01:00
const emitResolver = getDiagnosticsProducingTypeChecker ( ) . getEmitResolver ( ( options . outFile || options . out ) ? undefined : sourceFile ) ;
2015-02-26 01:45:45 +01:00
2016-08-15 20:07:49 +02:00
performance . mark ( "beforeEmit" ) ;
2015-02-05 03:42:44 +01:00
2017-02-07 23:36:15 +01:00
const transformers = emitOnlyDtsFiles ? [ ] : getTransformers ( options , customTransformers ) ;
2015-11-04 23:02:33 +01:00
const emitResult = emitFiles (
2015-02-26 01:45:45 +01:00
emitResolver ,
2015-02-05 23:41:04 +01:00
getEmitHost ( writeFileCallback ) ,
2016-08-24 01:11:52 +02:00
sourceFile ,
2017-02-07 23:36:15 +01:00
emitOnlyDtsFiles ,
transformers ) ;
2015-02-05 03:42:44 +01:00
2016-08-15 20:07:49 +02:00
performance . mark ( "afterEmit" ) ;
performance . measure ( "Emit" , "beforeEmit" , "afterEmit" ) ;
2015-02-06 00:50:18 +01:00
return emitResult ;
2015-01-15 22:22:23 +01:00
}
2015-02-06 00:50:18 +01:00
2015-10-15 23:43:51 +02:00
function getSourceFile ( fileName : string ) : SourceFile {
2016-05-11 08:43:26 +02:00
return getSourceFileByPath ( toPath ( fileName , currentDirectory , getCanonicalFileName ) ) ;
}
function getSourceFileByPath ( path : Path ) : SourceFile {
return filesByName . get ( path ) ;
2014-12-16 22:14:14 +01:00
}
2015-06-18 19:52:19 +02:00
function getDiagnosticsHelper (
2016-02-23 21:48:31 +01:00
sourceFile : SourceFile ,
getDiagnostics : ( sourceFile : SourceFile , cancellationToken : CancellationToken ) = > Diagnostic [ ] ,
cancellationToken : CancellationToken ) : Diagnostic [ ] {
2015-02-05 10:47:29 +01:00
if ( sourceFile ) {
2015-06-18 19:52:19 +02:00
return getDiagnostics ( sourceFile , cancellationToken ) ;
2014-12-16 22:52:47 +01:00
}
2015-02-05 10:47:29 +01:00
2015-11-04 23:02:33 +01:00
const allDiagnostics : Diagnostic [ ] = [ ] ;
2015-02-05 10:47:29 +01:00
forEach ( program . getSourceFiles ( ) , sourceFile = > {
2015-06-18 19:52:19 +02:00
if ( cancellationToken ) {
cancellationToken . throwIfCancellationRequested ( ) ;
}
addRange ( allDiagnostics , getDiagnostics ( sourceFile , cancellationToken ) ) ;
2015-02-05 10:47:29 +01:00
} ) ;
return sortAndDeduplicateDiagnostics ( allDiagnostics ) ;
2014-12-16 22:52:47 +01:00
}
2015-06-18 21:04:26 +02:00
function getSyntacticDiagnostics ( sourceFile : SourceFile , cancellationToken : CancellationToken ) : Diagnostic [ ] {
2015-06-18 19:52:19 +02:00
return getDiagnosticsHelper ( sourceFile , getSyntacticDiagnosticsForFile , cancellationToken ) ;
2014-12-16 22:14:14 +01:00
}
2015-06-18 21:04:26 +02:00
function getSemanticDiagnostics ( sourceFile : SourceFile , cancellationToken : CancellationToken ) : 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
2015-06-18 21:04:26 +02:00
function getDeclarationDiagnostics ( sourceFile : SourceFile , cancellationToken : CancellationToken ) : Diagnostic [ ] {
2016-02-24 23:21:30 +01:00
const options = program . getCompilerOptions ( ) ;
2016-02-24 23:30:21 +01:00
// collect diagnostics from the program only once if either no source file was specified or out/outFile is set (bundled emit)
2016-02-24 23:21:30 +01:00
if ( ! sourceFile || options . out || options . outFile ) {
return getDeclarationDiagnosticsWorker ( sourceFile , cancellationToken ) ;
}
else {
return getDiagnosticsHelper ( sourceFile , getDeclarationDiagnosticsForFile , cancellationToken ) ;
}
2015-03-20 00:55:07 +01:00
}
2016-10-10 22:06:58 +02:00
function getSyntacticDiagnosticsForFile ( sourceFile : SourceFile ) : Diagnostic [ ] {
2016-10-25 01:57:43 +02:00
// For JavaScript files, we report semantic errors for using TypeScript-only
// constructs from within a JavaScript file as syntactic errors.
2016-10-28 01:38:59 +02:00
if ( isSourceFileJavaScript ( sourceFile ) ) {
if ( ! sourceFile . additionalSyntacticDiagnostics ) {
sourceFile . additionalSyntacticDiagnostics = getJavaScriptSyntacticDiagnosticsForFile ( sourceFile ) ;
}
return concatenate ( sourceFile . additionalSyntacticDiagnostics , sourceFile . parseDiagnostics ) ;
2016-10-25 01:57:43 +02:00
}
2015-02-05 22:38:11 +01:00
return sourceFile . parseDiagnostics ;
2014-12-16 22:14:14 +01:00
}
2015-06-18 19:52:19 +02:00
function runWithCancellationToken < T > ( func : ( ) = > T ) : T {
try {
return func ( ) ;
}
catch ( e ) {
if ( e instanceof OperationCanceledException ) {
2015-07-09 00:35:49 +02:00
// We were canceled while performing the operation. Because our type checker
2015-06-18 19:52:19 +02:00
// might be a bad state, we need to throw it away.
2015-07-07 00:31:22 +02:00
//
2016-02-11 17:56:45 +01:00
// Note: we are overly aggressive here. We do not actually *have* to throw away
2015-07-07 00:31:22 +02:00
// the "noDiagnosticsTypeChecker". However, for simplicity, i'd like to keep
// the lifetimes of these two TypeCheckers the same. Also, we generally only
// cancel when the user has made a change anyways. And, in that case, we (the
2015-07-09 00:35:49 +02:00
// program instance) will get thrown away anyways. So trying to keep one of
2015-07-07 00:31:22 +02:00
// these type checkers alive doesn't serve much purpose.
2015-06-18 19:52:19 +02:00
noDiagnosticsTypeChecker = undefined ;
diagnosticsProducingTypeChecker = undefined ;
}
2015-02-05 10:47:29 +01:00
2015-06-18 19:52:19 +02:00
throw e ;
}
}
2015-02-05 10:47:29 +01:00
2015-06-18 21:04:26 +02:00
function getSemanticDiagnosticsForFile ( sourceFile : SourceFile , cancellationToken : CancellationToken ) : Diagnostic [ ] {
2017-03-07 22:26:41 +01:00
return getAndCacheDiagnostics ( sourceFile , cancellationToken , cachedSemanticDiagnosticsForFile , getSemanticDiagnosticsForFileNoCache ) ;
}
function getSemanticDiagnosticsForFileNoCache ( sourceFile : SourceFile , cancellationToken : CancellationToken ) : Diagnostic [ ] {
2015-06-18 19:52:19 +02:00
return runWithCancellationToken ( ( ) = > {
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 ) ;
2015-11-04 23:02:33 +01:00
const bindDiagnostics = sourceFile . bindDiagnostics ;
2017-03-08 08:03:47 +01:00
// For JavaScript files, we don't want to report semantic errors unless explicitly requested.
2017-03-15 23:17:33 +01:00
const includeCheckDiagnostics = ! isSourceFileJavaScript ( sourceFile ) || isCheckJsEnabledForFile ( sourceFile , options ) ;
2017-01-07 04:03:17 +01:00
const checkDiagnostics = includeCheckDiagnostics ? typeChecker . getDiagnostics ( sourceFile , cancellationToken ) : [ ] ;
2015-11-04 23:02:33 +01:00
const fileProcessingDiagnosticsInFile = fileProcessingDiagnostics . getDiagnostics ( sourceFile . fileName ) ;
const programDiagnosticsInFile = programDiagnostics . getDiagnostics ( sourceFile . fileName ) ;
2015-06-18 19:52:19 +02:00
2017-03-08 08:03:47 +01:00
const diagnostics = bindDiagnostics . concat ( checkDiagnostics , fileProcessingDiagnosticsInFile , programDiagnosticsInFile ) ;
return isSourceFileJavaScript ( sourceFile )
? filter ( diagnostics , shouldReportDiagnostic )
: diagnostics ;
2015-06-18 19:52:19 +02:00
} ) ;
2014-12-16 22:14:14 +01:00
}
2017-03-08 08:03:47 +01:00
/ * *
2017-03-23 00:23:21 +01:00
* Skip errors if previous line start with '// @ts-ignore' comment , not counting non - empty non - comment lines
2017-03-08 08:03:47 +01:00
* /
function shouldReportDiagnostic ( diagnostic : Diagnostic ) {
const { file , start } = diagnostic ;
const lineStarts = getLineStarts ( file ) ;
let { line } = computeLineAndCharacterOfPosition ( lineStarts , start ) ;
while ( line > 0 ) {
const previousLineText = file . text . slice ( lineStarts [ line - 1 ] , lineStarts [ line ] ) ;
2017-03-23 00:23:21 +01:00
const result = ignoreDiagnosticCommentRegEx . exec ( previousLineText ) ;
2017-03-08 08:03:47 +01:00
if ( ! result ) {
// non-empty line
return true ;
}
if ( result [ 3 ] ) {
2017-03-23 00:23:21 +01:00
// @ts-ignore
2017-03-08 08:03:47 +01:00
return false ;
}
line -- ;
}
return true ;
}
2016-10-28 01:38:59 +02:00
function getJavaScriptSyntacticDiagnosticsForFile ( sourceFile : SourceFile ) : Diagnostic [ ] {
2015-09-09 23:41:19 +02:00
return runWithCancellationToken ( ( ) = > {
2015-11-06 21:39:42 +01:00
const diagnostics : Diagnostic [ ] = [ ] ;
2016-11-01 20:54:27 +01:00
let parent : Node = sourceFile ;
2015-09-09 23:41:19 +02:00
walk ( sourceFile ) ;
return diagnostics ;
2016-11-01 20:54:27 +01:00
function walk ( node : Node ) {
// Return directly from the case if the given node doesnt want to visit each child
// Otherwise break to visit each child
switch ( parent . kind ) {
case SyntaxKind . Parameter :
case SyntaxKind . PropertyDeclaration :
if ( ( < ParameterDeclaration | PropertyDeclaration > parent ) . questionToken === node ) {
diagnostics . push ( createDiagnosticForNode ( node , Diagnostics . _0_can_only_be_used_in_a_ts_file , "?" ) ) ;
return ;
}
// Pass through
case SyntaxKind . MethodDeclaration :
case SyntaxKind . MethodSignature :
case SyntaxKind . Constructor :
case SyntaxKind . GetAccessor :
case SyntaxKind . SetAccessor :
case SyntaxKind . FunctionExpression :
case SyntaxKind . FunctionDeclaration :
case SyntaxKind . ArrowFunction :
case SyntaxKind . FunctionDeclaration :
case SyntaxKind . VariableDeclaration :
// type annotation
if ( ( < FunctionLikeDeclaration | VariableDeclaration | ParameterDeclaration | PropertyDeclaration > parent ) . type === node ) {
diagnostics . push ( createDiagnosticForNode ( node , Diagnostics . types_can_only_be_used_in_a_ts_file ) ) ;
return ;
}
2015-09-09 23:41:19 +02:00
}
switch ( node . kind ) {
case SyntaxKind . ImportEqualsDeclaration :
diagnostics . push ( createDiagnosticForNode ( node , Diagnostics . import_can_only_be_used_in_a_ts_file ) ) ;
2016-11-01 20:54:27 +01:00
return ;
2015-09-09 23:41:19 +02:00
case SyntaxKind . ExportAssignment :
2016-02-01 22:28:58 +01:00
if ( ( < ExportAssignment > node ) . isExportEquals ) {
diagnostics . push ( createDiagnosticForNode ( node , Diagnostics . export_can_only_be_used_in_a_ts_file ) ) ;
2016-11-01 20:54:27 +01:00
return ;
2015-09-09 23:41:19 +02:00
}
break ;
case SyntaxKind . HeritageClause :
2016-12-18 07:44:54 +01:00
const heritageClause = < HeritageClause > node ;
2015-09-09 23:41:19 +02:00
if ( heritageClause . token === SyntaxKind . ImplementsKeyword ) {
diagnostics . push ( createDiagnosticForNode ( node , Diagnostics . implements_clauses_can_only_be_used_in_a_ts_file ) ) ;
2016-11-01 20:54:27 +01:00
return ;
2015-09-09 23:41:19 +02:00
}
break ;
case SyntaxKind . InterfaceDeclaration :
diagnostics . push ( createDiagnosticForNode ( node , Diagnostics . interface_declarations_can_only_be_used_in_a_ts_file ) ) ;
2016-11-01 20:54:27 +01:00
return ;
2015-09-09 23:41:19 +02:00
case SyntaxKind . ModuleDeclaration :
diagnostics . push ( createDiagnosticForNode ( node , Diagnostics . module _declarations_can_only_be_used_in_a_ts_file ) ) ;
2016-11-01 20:54:27 +01:00
return ;
2015-09-09 23:41:19 +02:00
case SyntaxKind . TypeAliasDeclaration :
diagnostics . push ( createDiagnosticForNode ( node , Diagnostics . type_aliases_can_only_be_used_in_a_ts_file ) ) ;
2016-11-01 20:54:27 +01:00
return ;
case SyntaxKind . EnumDeclaration :
diagnostics . push ( createDiagnosticForNode ( node , Diagnostics . enum_declarations_can_only_be_used_in_a_ts_file ) ) ;
return ;
case SyntaxKind . TypeAssertionExpression :
2016-12-18 07:44:54 +01:00
const typeAssertionExpression = < TypeAssertion > node ;
2016-11-01 20:54:27 +01:00
diagnostics . push ( createDiagnosticForNode ( typeAssertionExpression . type , Diagnostics . type_assertion_expressions_can_only_be_used_in_a_ts_file ) ) ;
return ;
}
const prevParent = parent ;
parent = node ;
forEachChild ( node , walk , walkArray ) ;
parent = prevParent ;
}
function walkArray ( nodes : NodeArray < Node > ) {
if ( parent . decorators === nodes && ! options . experimentalDecorators ) {
diagnostics . push ( createDiagnosticForNode ( parent , Diagnostics . Experimental_support_for_decorators_is_a_feature_that_is_subject_to_change_in_a_future_release_Set_the_experimentalDecorators_option_to_remove_this_warning ) ) ;
}
switch ( parent . kind ) {
case SyntaxKind . ClassDeclaration :
2015-09-09 23:41:19 +02:00
case SyntaxKind . MethodDeclaration :
case SyntaxKind . MethodSignature :
case SyntaxKind . Constructor :
case SyntaxKind . GetAccessor :
case SyntaxKind . SetAccessor :
case SyntaxKind . FunctionExpression :
case SyntaxKind . FunctionDeclaration :
case SyntaxKind . ArrowFunction :
case SyntaxKind . FunctionDeclaration :
2016-11-01 20:54:27 +01:00
// Check type parameters
if ( nodes === ( < ClassDeclaration | FunctionLikeDeclaration > parent ) . typeParameters ) {
diagnostics . push ( createDiagnosticForNodeArray ( nodes , Diagnostics . type_parameter_declarations_can_only_be_used_in_a_ts_file ) ) ;
return ;
2015-09-09 23:41:19 +02:00
}
2016-11-09 01:28:10 +01:00
// pass through
2015-09-09 23:41:19 +02:00
case SyntaxKind . VariableStatement :
2016-11-01 20:54:27 +01:00
// Check modifiers
if ( nodes === ( < ClassDeclaration | FunctionLikeDeclaration | VariableStatement > parent ) . modifiers ) {
2016-11-10 17:53:32 +01:00
return checkModifiers ( < NodeArray < Modifier > > nodes , parent . kind === SyntaxKind . VariableStatement ) ;
2015-09-09 23:41:19 +02:00
}
break ;
case SyntaxKind . PropertyDeclaration :
2016-11-01 20:54:27 +01:00
// Check modifiers of property declaration
if ( nodes === ( < PropertyDeclaration > parent ) . modifiers ) {
for ( const modifier of < NodeArray < Modifier > > nodes ) {
2016-06-15 18:42:52 +02:00
if ( modifier . kind !== SyntaxKind . StaticKeyword ) {
diagnostics . push ( createDiagnosticForNode ( modifier , Diagnostics . _0_can_only_be_used_in_a_ts_file , tokenToString ( modifier . kind ) ) ) ;
}
}
2016-11-01 20:54:27 +01:00
return ;
2016-06-15 18:42:52 +02:00
}
2016-11-01 20:54:27 +01:00
break ;
case SyntaxKind . Parameter :
// Check modifiers of parameter declaration
if ( nodes === ( < ParameterDeclaration > parent ) . modifiers ) {
diagnostics . push ( createDiagnosticForNodeArray ( nodes , Diagnostics . parameter_modifiers_can_only_be_used_in_a_ts_file ) ) ;
return ;
2016-06-15 18:42:52 +02:00
}
break ;
2016-11-01 20:54:27 +01:00
case SyntaxKind . CallExpression :
case SyntaxKind . NewExpression :
case SyntaxKind . ExpressionWithTypeArguments :
// Check type arguments
if ( nodes === ( < CallExpression | NewExpression | ExpressionWithTypeArguments > parent ) . typeArguments ) {
diagnostics . push ( createDiagnosticForNodeArray ( nodes , Diagnostics . type_arguments_can_only_be_used_in_a_ts_file ) ) ;
return ;
2016-02-13 04:19:23 +01:00
}
2016-11-01 20:54:27 +01:00
break ;
2015-09-09 23:41:19 +02:00
}
2016-11-01 20:54:27 +01:00
for ( const node of nodes ) {
walk ( node ) ;
2015-09-09 23:41:19 +02:00
}
}
2016-11-09 01:28:10 +01:00
function checkModifiers ( modifiers : NodeArray < Modifier > , isConstValid : boolean ) {
2016-11-01 20:54:27 +01:00
for ( const modifier of modifiers ) {
switch ( modifier . kind ) {
2016-11-09 01:28:10 +01:00
case SyntaxKind . ConstKeyword :
if ( isConstValid ) {
continue ;
}
// Fallthrough to report error
2016-11-01 20:54:27 +01:00
case SyntaxKind . PublicKeyword :
case SyntaxKind . PrivateKeyword :
case SyntaxKind . ProtectedKeyword :
case SyntaxKind . ReadonlyKeyword :
case SyntaxKind . DeclareKeyword :
2016-11-09 01:28:10 +01:00
case SyntaxKind . AbstractKeyword :
2016-11-01 20:54:27 +01:00
diagnostics . push ( createDiagnosticForNode ( modifier , Diagnostics . _0_can_only_be_used_in_a_ts_file , tokenToString ( modifier . kind ) ) ) ;
break ;
// These are all legal modifiers.
case SyntaxKind . StaticKeyword :
case SyntaxKind . ExportKeyword :
case SyntaxKind . DefaultKeyword :
2015-09-09 23:41:19 +02:00
}
}
}
2016-11-01 20:54:27 +01:00
function createDiagnosticForNodeArray ( nodes : NodeArray < Node > , message : DiagnosticMessage , arg0? : string | number , arg1? : string | number , arg2? : string | number ) : Diagnostic {
const start = nodes . pos ;
return createFileDiagnostic ( sourceFile , start , nodes . end - start , message , arg0 , arg1 , arg2 ) ;
2015-09-09 23:41:19 +02:00
}
2016-11-01 19:05:09 +01:00
// Since these are syntactic diagnostics, parent might not have been set
// this means the sourceFile cannot be infered from the node
function createDiagnosticForNode ( node : Node , message : DiagnosticMessage , arg0? : string | number , arg1? : string | number , arg2? : string | number ) : Diagnostic {
return createDiagnosticForNodeInSourceFile ( sourceFile , node , message , arg0 , arg1 , arg2 ) ;
2015-09-09 23:41:19 +02:00
}
} ) ;
}
2017-03-07 22:26:41 +01:00
function getDeclarationDiagnosticsWorker ( sourceFile : SourceFile | undefined , cancellationToken : CancellationToken ) : Diagnostic [ ] {
return getAndCacheDiagnostics ( sourceFile , cancellationToken , cachedDeclarationDiagnosticsForFile , getDeclarationDiagnosticsForFileNoCache ) ;
}
2017-03-17 21:37:12 +01:00
function getDeclarationDiagnosticsForFileNoCache ( sourceFile : SourceFile | undefined , cancellationToken : CancellationToken ) {
2015-06-18 19:52:19 +02:00
return runWithCancellationToken ( ( ) = > {
2016-02-24 23:21:30 +01:00
const resolver = getDiagnosticsProducingTypeChecker ( ) . getEmitResolver ( sourceFile , cancellationToken ) ;
// Don't actually write any files since we're just getting diagnostics.
2016-10-19 23:15:14 +02:00
return ts . getDeclarationDiagnostics ( getEmitHost ( noop ) , resolver , sourceFile ) ;
2015-06-18 19:52:19 +02:00
} ) ;
2015-03-20 00:55:07 +01:00
}
2017-03-07 22:26:41 +01:00
function getAndCacheDiagnostics (
sourceFile : SourceFile | undefined ,
cancellationToken : CancellationToken ,
cache : DiagnosticCache ,
getDiagnostics : ( sourceFile : SourceFile , cancellationToken : CancellationToken ) = > Diagnostic [ ] ) {
const cachedResult = sourceFile
? cache . perFile && cache . perFile . get ( sourceFile . path )
: cache . allDiagnostics ;
if ( cachedResult ) {
return cachedResult ;
}
const result = getDiagnostics ( sourceFile , cancellationToken ) || emptyArray ;
if ( sourceFile ) {
if ( ! cache . perFile ) {
cache . perFile = createFileMap < Diagnostic [ ] > ( ) ;
}
cache . perFile . set ( sourceFile . path , result ) ;
}
else {
cache . allDiagnostics = result ;
}
return result ;
}
2016-02-24 23:21:30 +01:00
function getDeclarationDiagnosticsForFile ( sourceFile : SourceFile , cancellationToken : CancellationToken ) : Diagnostic [ ] {
return isDeclarationFile ( sourceFile ) ? [ ] : getDeclarationDiagnosticsWorker ( sourceFile , cancellationToken ) ;
}
2015-06-18 18:32:52 +02:00
function getOptionsDiagnostics ( ) : Diagnostic [ ] {
2015-11-04 23:02:33 +01:00
const allDiagnostics : Diagnostic [ ] = [ ] ;
2015-10-01 01:10:52 +02:00
addRange ( allDiagnostics , fileProcessingDiagnostics . getGlobalDiagnostics ( ) ) ;
2015-09-10 19:46:39 +02:00
addRange ( allDiagnostics , programDiagnostics . getGlobalDiagnostics ( ) ) ;
2015-05-27 05:18:13 +02:00
return sortAndDeduplicateDiagnostics ( allDiagnostics ) ;
}
2014-12-16 22:14:14 +01:00
function getGlobalDiagnostics ( ) : Diagnostic [ ] {
2015-11-04 23:02:33 +01:00
const allDiagnostics : Diagnostic [ ] = [ ] ;
2015-06-18 18:32:52 +02:00
addRange ( allDiagnostics , getDiagnosticsProducingTypeChecker ( ) . getGlobalDiagnostics ( ) ) ;
2015-02-05 10:47:29 +01:00
return sortAndDeduplicateDiagnostics ( allDiagnostics ) ;
2014-12-16 22:14:14 +01:00
}
2015-02-04 01:08:46 +01:00
function processRootFile ( fileName : string , isDefaultLib : boolean ) {
2016-10-10 22:06:58 +02:00
processSourceFile ( normalizePath ( fileName ) , isDefaultLib ) ;
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
2015-06-23 02:48:44 +02:00
function module NameIsEqualTo ( a : LiteralExpression , b : LiteralExpression ) : boolean {
2015-06-25 02:40:04 +02:00
return a . text === b . text ;
2015-06-23 02:48:44 +02:00
}
2015-10-01 01:10:52 +02:00
2016-01-14 19:56:49 +01:00
function getTextOfLiteral ( literal : LiteralExpression ) : string {
return literal . text ;
}
2015-06-23 02:48:44 +02:00
function collectExternalModuleReferences ( file : SourceFile ) : void {
if ( file . imports ) {
return ;
}
2015-10-01 01:10:52 +02:00
2015-11-09 21:49:36 +01:00
const isJavaScriptFile = isSourceFileJavaScript ( file ) ;
2015-12-22 22:21:51 +01:00
const isExternalModuleFile = isExternalModule ( file ) ;
2016-11-02 22:41:26 +01:00
const isDtsFile = isDeclarationFile ( file ) ;
2015-10-15 02:36:03 +02:00
2015-06-23 02:48:44 +02:00
let imports : LiteralExpression [ ] ;
2015-12-22 22:21:51 +01:00
let module Augmentations : LiteralExpression [ ] ;
2016-11-02 22:41:26 +01:00
let ambientModules : string [ ] ;
2015-12-22 22:21:51 +01:00
2016-06-15 01:24:01 +02:00
// If we are importing helpers, we need to add a synthetic reference to resolve the
// helpers library.
if ( options . importHelpers
&& ( options . isolatedModules || isExternalModuleFile )
&& ! file . isDeclarationFile ) {
2016-11-10 20:57:37 +01:00
// synthesize 'import "tslib"' declaration
2017-02-02 01:36:10 +01:00
const externalHelpersModuleReference = createLiteral ( externalHelpersModuleNameText ) ;
const importDecl = createImportDeclaration ( undefined , undefined , undefined ) ;
2016-11-10 20:57:37 +01:00
externalHelpersModuleReference . parent = importDecl ;
2017-02-02 01:36:10 +01:00
importDecl . parent = file ;
2016-06-15 01:24:01 +02:00
imports = [ externalHelpersModuleReference ] ;
}
2015-11-04 23:02:33 +01:00
for ( const node of file . statements ) {
2015-12-22 22:21:51 +01:00
collectModuleReferences ( node , /*inAmbientModule*/ false ) ;
if ( isJavaScriptFile ) {
collectRequireCalls ( node ) ;
}
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 ;
2015-12-22 22:21:51 +01:00
function collectModuleReferences ( node : Node , inAmbientModule : boolean ) : void {
switch ( node . kind ) {
case SyntaxKind . ImportDeclaration :
case SyntaxKind . ImportEqualsDeclaration :
case SyntaxKind . ExportDeclaration :
2016-12-18 07:44:54 +01:00
const module NameExpr = getExternalModuleName ( node ) ;
2015-12-22 22:21:51 +01:00
if ( ! module NameExpr || module NameExpr.kind !== SyntaxKind . StringLiteral ) {
break ;
}
if ( ! ( < LiteralExpression > module NameExpr ) . text ) {
break ;
}
2015-06-23 02:48:44 +02:00
2015-12-22 22:21:51 +01:00
// TypeScript 1.0 spec (April 2014): 12.1.6
2016-01-30 20:37:02 +01:00
// An ExternalImportDeclaration in an AmbientExternalModuleDeclaration may reference other external modules
2015-12-22 22:21:51 +01:00
// only through top - level external module names. Relative external module names are not permitted.
if ( ! inAmbientModule || ! isExternalModuleNameRelative ( ( < LiteralExpression > module NameExpr ) . text ) ) {
( imports || ( imports = [ ] ) ) . push ( < LiteralExpression > module NameExpr ) ;
}
break ;
case SyntaxKind . ModuleDeclaration :
2016-03-10 20:12:24 +01:00
if ( isAmbientModule ( < ModuleDeclaration > node ) && ( inAmbientModule || hasModifier ( node , ModifierFlags . Ambient ) || isDeclarationFile ( file ) ) ) {
2015-12-22 22:21:51 +01:00
const module Name = < LiteralExpression > ( < ModuleDeclaration > 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 ( module Name.text ) ) ) {
( module Augmentations || ( module Augmentations = [ ] ) ) . push ( module Name ) ;
2015-10-15 02:36:03 +02:00
}
2015-12-22 22:21:51 +01:00
else if ( ! inAmbientModule ) {
2016-11-02 22:41:26 +01:00
if ( isDtsFile ) {
// for global .d.ts files record name of ambient module
( ambientModules || ( ambientModules = [ ] ) ) . push ( module Name.text ) ;
}
2016-01-30 20:37:02 +01:00
// An AmbientExternalModuleDeclaration declares an external module.
2015-10-22 23:12:57 +02:00
// This type of declaration is permitted only in the global module.
// The StringLiteral must specify a top - level external module name.
// Relative external module names are not permitted
2015-12-22 22:21:51 +01:00
2016-06-02 18:15:48 +02:00
// NOTE: body of ambient module is always a module block, if it exists
const body = < ModuleBlock > ( < ModuleDeclaration > node ) . body ;
if ( body ) {
for ( const statement of body . statements ) {
collectModuleReferences ( statement , /*inAmbientModule*/ true ) ;
}
2015-12-22 22:21:51 +01:00
}
2015-10-22 23:12:57 +02:00
}
2015-12-22 22:21:51 +01:00
}
2015-06-23 02:48:44 +02:00
}
2015-12-22 22:21:51 +01:00
}
2015-06-23 02:48:44 +02:00
2015-12-22 22:21:51 +01:00
function collectRequireCalls ( node : Node ) : void {
2016-01-28 23:18:23 +01:00
if ( isRequireCall ( node , /*checkArgumentIsStringLiteral*/ true ) ) {
2015-12-22 22:21:51 +01:00
( imports || ( imports = [ ] ) ) . push ( < StringLiteral > ( < CallExpression > node ) . arguments [ 0 ] ) ;
}
else {
forEachChild ( node , collectRequireCalls ) ;
2015-06-23 02:48:44 +02:00
}
}
2014-12-16 22:14:14 +01:00
}
2016-10-10 22:06:58 +02:00
function processSourceFile ( fileName : string , isDefaultLib : boolean , refFile? : SourceFile , refPos? : number , refEnd? : number ) {
2015-05-05 08:30:43 +02:00
let diagnosticArgument : string [ ] ;
2015-03-13 23:03:17 +01:00
let diagnostic : DiagnosticMessage ;
2015-02-04 01:08:46 +01:00
if ( hasExtension ( fileName ) ) {
2015-03-27 00:48:17 +01:00
if ( ! options . allowNonTsExtensions && ! forEach ( supportedExtensions , extension = > fileExtensionIs ( host . getCanonicalFileName ( fileName ) , extension ) ) ) {
2015-05-05 08:30:43 +02:00
diagnostic = Diagnostics . File_0_has_unsupported_extension_The_only_supported_extensions_are_1 ;
diagnosticArgument = [ fileName , "'" + supportedExtensions . join ( "', '" ) + "'" ] ;
2014-12-16 22:14:14 +01:00
}
2016-10-10 22:06:58 +02:00
else if ( ! findSourceFile ( fileName , toPath ( fileName , currentDirectory , getCanonicalFileName ) , isDefaultLib , refFile , refPos , refEnd ) ) {
2014-12-16 22:14:14 +01:00
diagnostic = Diagnostics . File_0_not_found ;
2015-05-05 08:30:43 +02:00
diagnosticArgument = [ fileName ] ;
2014-12-16 22:14:14 +01:00
}
2015-02-04 01:08:46 +01:00
else if ( refFile && host . getCanonicalFileName ( fileName ) === host . getCanonicalFileName ( refFile . fileName ) ) {
2014-12-16 22:14:14 +01:00
diagnostic = Diagnostics . A_file_cannot_have_a_reference_to_itself ;
2015-05-05 08:30:43 +02:00
diagnosticArgument = [ fileName ] ;
2014-12-16 22:14:14 +01:00
}
}
else {
2016-10-10 22:06:58 +02:00
const nonTsFile : SourceFile = options . allowNonTsExtensions && findSourceFile ( fileName , toPath ( fileName , currentDirectory , getCanonicalFileName ) , isDefaultLib , refFile , refPos , refEnd ) ;
2015-06-11 23:30:58 +02:00
if ( ! nonTsFile ) {
if ( options . allowNonTsExtensions ) {
diagnostic = Diagnostics . File_0_not_found ;
diagnosticArgument = [ fileName ] ;
}
2016-10-10 22:06:58 +02:00
else if ( ! forEach ( supportedExtensions , extension = > findSourceFile ( fileName + extension , toPath ( fileName + extension , currentDirectory , getCanonicalFileName ) , isDefaultLib , refFile , refPos , refEnd ) ) ) {
2015-06-11 23:30:58 +02:00
diagnostic = Diagnostics . File_0_not_found ;
fileName += ".ts" ;
diagnosticArgument = [ fileName ] ;
}
2014-12-16 22:14:14 +01:00
}
}
if ( diagnostic ) {
2015-08-12 22:04:10 +02:00
if ( refFile !== undefined && refEnd !== undefined && refPos !== undefined ) {
2015-09-10 19:46:39 +02:00
fileProcessingDiagnostics . add ( createFileDiagnostic ( refFile , refPos , refEnd - refPos , diagnostic , . . . diagnosticArgument ) ) ;
2014-12-16 22:14:14 +01:00
}
else {
2015-09-10 19:46:39 +02:00
fileProcessingDiagnostics . add ( createCompilerDiagnostic ( diagnostic , . . . diagnosticArgument ) ) ;
2014-12-16 22:14:14 +01:00
}
}
}
2015-10-15 23:43:51 +02:00
function reportFileNamesDifferOnlyInCasingError ( fileName : string , existingFileName : string , refFile : SourceFile , refPos : number , refEnd : number ) : void {
if ( refFile !== undefined && refPos !== undefined && refEnd !== undefined ) {
fileProcessingDiagnostics . add ( createFileDiagnostic ( refFile , refPos , refEnd - refPos ,
Diagnostics . File_name_0_differs_from_already_included_file_name_1_only_in_casing , fileName , existingFileName ) ) ;
2014-12-16 22:14:14 +01:00
}
2015-10-15 23:43:51 +02:00
else {
fileProcessingDiagnostics . add ( createCompilerDiagnostic ( Diagnostics . File_name_0_differs_from_already_included_file_name_1_only_in_casing , fileName , existingFileName ) ) ;
2015-09-27 06:29:07 +02:00
}
2015-10-15 23:43:51 +02:00
}
2015-10-01 01:10:52 +02:00
2015-10-15 23:43:51 +02:00
// Get source file from normalized fileName
2016-10-10 22:06:58 +02:00
function findSourceFile ( fileName : string , path : Path , isDefaultLib : boolean , refFile? : SourceFile , refPos? : number , refEnd? : number ) : SourceFile {
2016-10-28 00:50:21 +02:00
if ( filesByName . contains ( path ) ) {
const file = filesByName . get ( path ) ;
2015-10-15 23:43:51 +02:00
// try to check if we've already seen this file but with a different casing in path
// NOTE: this only makes sense for case-insensitive file systems
2015-11-16 18:49:58 +01:00
if ( file && options . forceConsistentCasingInFileNames && getNormalizedAbsolutePath ( file . fileName , currentDirectory ) !== getNormalizedAbsolutePath ( fileName , currentDirectory ) ) {
2015-10-15 23:43:51 +02:00
reportFileNamesDifferOnlyInCasingError ( fileName , file . fileName , refFile , refPos , refEnd ) ;
}
2016-07-11 01:11:42 +02:00
// If the file was previously found via a node_modules search, but is now being processed as a root file,
// then everything it sucks in may also be marked incorrectly, and needs to be checked again.
2016-12-05 23:13:32 +01:00
if ( file && sourceFilesFoundSearchingNodeModules . get ( file . path ) && currentNodeModulesDepth == 0 ) {
sourceFilesFoundSearchingNodeModules . set ( file . path , false ) ;
2016-07-11 01:11:42 +02:00
if ( ! options . noResolve ) {
2016-10-10 22:06:58 +02:00
processReferencedFiles ( file , isDefaultLib ) ;
2016-07-11 01:11:42 +02:00
processTypeReferenceDirectives ( file ) ;
}
2016-12-05 23:13:32 +01:00
module sWithElidedImports.set ( file . path , false ) ;
2016-10-10 22:06:58 +02:00
processImportedModules ( file ) ;
2016-07-11 01:11:42 +02:00
}
2016-06-27 05:48:22 +02:00
// See if we need to reprocess the imports due to prior skipped imports
2016-12-05 23:13:32 +01:00
else if ( file && module sWithElidedImports.get ( file . path ) ) {
2016-10-13 22:02:22 +02:00
if ( currentNodeModulesDepth < maxNodeModuleJsDepth ) {
2016-12-05 23:13:32 +01:00
module sWithElidedImports.set ( file . path , false ) ;
2016-10-10 22:06:58 +02:00
processImportedModules ( file ) ;
2016-06-27 05:48:22 +02:00
}
}
2015-09-27 06:29:07 +02:00
return file ;
2014-12-16 22:14:14 +01:00
}
2015-09-27 06:29:07 +02:00
// We haven't looked for this file, do so now and cache result
2016-10-28 00:50:21 +02:00
const file = host . getSourceFile ( fileName , options . target , hostErrorMessage = > {
2015-09-27 06:29:07 +02:00
if ( refFile !== undefined && refPos !== undefined && refEnd !== undefined ) {
fileProcessingDiagnostics . add ( createFileDiagnostic ( refFile , refPos , refEnd - refPos ,
Diagnostics . Cannot_read_file_0_Colon_1 , fileName , hostErrorMessage ) ) ;
}
else {
fileProcessingDiagnostics . add ( createCompilerDiagnostic ( Diagnostics . Cannot_read_file_0_Colon_1 , fileName , hostErrorMessage ) ) ;
}
} ) ;
2015-10-01 01:10:52 +02:00
2015-11-16 18:49:58 +01:00
filesByName . set ( path , file ) ;
2015-09-27 06:29:07 +02:00
if ( file ) {
2016-12-05 23:13:32 +01:00
sourceFilesFoundSearchingNodeModules . set ( path , currentNodeModulesDepth > 0 ) ;
2015-11-16 18:49:58 +01:00
file . path = path ;
2015-10-29 22:54:56 +01:00
2015-10-15 23:43:51 +02:00
if ( host . useCaseSensitiveFileNames ( ) ) {
// for case-sensitive file systems check if we've already seen some file with similar filename ignoring case
2015-11-16 18:49:58 +01:00
const existingFile = filesByNameIgnoreCase . get ( path ) ;
2015-10-15 23:43:51 +02:00
if ( existingFile ) {
reportFileNamesDifferOnlyInCasingError ( fileName , existingFile . fileName , refFile , refPos , refEnd ) ;
}
else {
2015-11-16 18:49:58 +01:00
filesByNameIgnoreCase . set ( path , file ) ;
2015-10-15 23:43:51 +02:00
}
}
2014-12-16 22:14:14 +01:00
2015-10-15 23:43:51 +02:00
skipDefaultLib = skipDefaultLib || file . hasNoDefaultLib ;
2015-10-01 01:10:52 +02:00
2015-09-27 06:29:07 +02:00
if ( ! options . noResolve ) {
2016-10-10 22:06:58 +02:00
processReferencedFiles ( file , isDefaultLib ) ;
2016-04-06 01:33:11 +02:00
processTypeReferenceDirectives ( file ) ;
2015-09-27 06:29:07 +02:00
}
2015-08-19 20:58:02 +02:00
2015-09-27 06:29:07 +02:00
// always process imported modules to record module name resolutions
2016-10-10 22:06:58 +02:00
processImportedModules ( file ) ;
2015-08-19 20:58:02 +02:00
2015-09-27 06:29:07 +02:00
if ( isDefaultLib ) {
files . unshift ( file ) ;
}
else {
files . push ( file ) ;
2014-12-16 22:14:14 +01:00
}
}
2015-09-27 06:29:07 +02:00
return file ;
2014-12-16 22:14:14 +01:00
}
2016-10-10 22:06:58 +02:00
function processReferencedFiles ( file : SourceFile , isDefaultLib : boolean ) {
2014-12-16 22:14:14 +01:00
forEach ( file . referencedFiles , ref = > {
2015-11-04 23:02:33 +01:00
const referencedFileName = resolveTripleslashReference ( ref . fileName , file . fileName ) ;
2016-10-10 22:06:58 +02:00
processSourceFile ( referencedFileName , isDefaultLib , file , ref . pos , ref . end ) ;
2014-12-16 22:14:14 +01:00
} ) ;
}
2015-10-01 01:10:52 +02:00
2016-04-06 01:33:11 +02:00
function processTypeReferenceDirectives ( file : SourceFile ) {
2016-08-17 15:21:49 +02:00
// We lower-case all type references because npm automatically lowercases all packages. See GH#9824.
2016-08-15 16:40:25 +02:00
const typeDirectives = map ( file . typeReferenceDirectives , ref = > ref . fileName . toLocaleLowerCase ( ) ) ;
2016-04-01 21:41:01 +02:00
const resolutions = resolveTypeReferenceDirectiveNamesWorker ( typeDirectives , file . fileName ) ;
2016-02-23 21:48:31 +01:00
2016-04-01 21:41:01 +02:00
for ( let i = 0 ; i < typeDirectives . length ; i ++ ) {
const ref = file . typeReferenceDirectives [ i ] ;
const resolvedTypeReferenceDirective = resolutions [ i ] ;
// store resolved type directive on the file
2016-08-15 16:40:25 +02:00
const fileName = ref . fileName . toLocaleLowerCase ( ) ;
setResolvedTypeReferenceDirective ( file , fileName , resolvedTypeReferenceDirective ) ;
processTypeReferenceDirective ( fileName , resolvedTypeReferenceDirective , file , ref . pos , ref . end ) ;
2016-04-06 22:49:25 +02:00
}
}
function processTypeReferenceDirective ( typeReferenceDirective : string , resolvedTypeReferenceDirective : ResolvedTypeReferenceDirective ,
refFile? : SourceFile , refPos? : number , refEnd? : number ) : void {
// If we already found this library as a primary reference - nothing to do
2016-12-05 23:13:32 +01:00
const previousResolution = resolvedTypeReferenceDirectives . get ( typeReferenceDirective ) ;
2016-04-06 22:49:25 +02:00
if ( previousResolution && previousResolution . primary ) {
return ;
}
let saveResolution = true ;
if ( resolvedTypeReferenceDirective ) {
if ( resolvedTypeReferenceDirective . primary ) {
// resolved from the primary path
2016-10-10 22:06:58 +02:00
processSourceFile ( resolvedTypeReferenceDirective . resolvedFileName , /*isDefaultLib*/ false , refFile , refPos , refEnd ) ;
2016-02-23 21:48:31 +01:00
}
2016-04-06 22:49:25 +02:00
else {
// If we already resolved to this file, it must have been a secondary reference. Check file contents
// for sameness and possibly issue an error
if ( previousResolution ) {
2016-11-09 16:41:25 +01:00
// Don't bother reading the file again if it's the same file.
if ( resolvedTypeReferenceDirective . resolvedFileName !== previousResolution . resolvedFileName ) {
const otherFileText = host . readFile ( resolvedTypeReferenceDirective . resolvedFileName ) ;
if ( otherFileText !== getSourceFile ( previousResolution . resolvedFileName ) . text ) {
fileProcessingDiagnostics . add ( createDiagnostic ( refFile , refPos , refEnd ,
Diagnostics . Conflicting_definitions_for_0_found_at_1_and_2_Consider_installing_a_specific_version_of_this_library_to_resolve_the_conflict ,
typeReferenceDirective ,
resolvedTypeReferenceDirective . resolvedFileName ,
previousResolution . resolvedFileName
) ) ;
}
2016-04-06 22:49:25 +02:00
}
// don't overwrite previous resolution result
saveResolution = false ;
2016-02-23 21:48:31 +01:00
}
2016-04-01 21:41:01 +02:00
else {
2016-04-06 22:49:25 +02:00
// First resolution of this library
2016-10-10 22:06:58 +02:00
processSourceFile ( resolvedTypeReferenceDirective . resolvedFileName , /*isDefaultLib*/ false , refFile , refPos , refEnd ) ;
2016-02-23 21:48:31 +01:00
}
}
2016-04-06 22:49:25 +02:00
}
else {
2016-06-11 00:44:11 +02:00
fileProcessingDiagnostics . add ( createDiagnostic ( refFile , refPos , refEnd , Diagnostics . Cannot_find_type_definition_file_for_0 , typeReferenceDirective ) ) ;
2016-04-06 22:49:25 +02:00
}
2016-02-23 21:48:31 +01:00
2016-04-06 22:49:25 +02:00
if ( saveResolution ) {
2016-12-05 23:13:32 +01:00
resolvedTypeReferenceDirectives . set ( typeReferenceDirective , resolvedTypeReferenceDirective ) ;
2016-04-06 22:49:25 +02:00
}
}
function createDiagnostic ( refFile : SourceFile , refPos : number , refEnd : number , message : DiagnosticMessage , . . . args : any [ ] ) : Diagnostic {
if ( refFile === undefined || refPos === undefined || refEnd === undefined ) {
return createCompilerDiagnostic ( message , . . . args ) ;
}
else {
return createFileDiagnostic ( refFile , refPos , refEnd - refPos , message , . . . args ) ;
2016-04-01 21:41:01 +02:00
}
2016-02-23 21:48:31 +01:00
}
2015-10-15 23:43:51 +02:00
function getCanonicalFileName ( fileName : string ) : string {
return host . getCanonicalFileName ( fileName ) ;
}
2016-10-10 22:06:58 +02:00
function processImportedModules ( file : SourceFile ) {
2015-08-20 00:37:37 +02:00
collectExternalModuleReferences ( file ) ;
2015-12-22 22:21:51 +01:00
if ( file . imports . length || file . module Augmentations.length ) {
2016-10-28 00:50:21 +02:00
file . resolvedModules = createMap < ResolvedModuleFull > ( ) ;
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.
const nonGlobalAugmentation = filter ( file . module Augmentations , ( module Augmentation ) = > module Augmentation.kind === SyntaxKind . StringLiteral ) ;
const module Names = map ( concatenate ( file . imports , nonGlobalAugmentation ) , getTextOfLiteral ) ;
2016-11-02 22:41:26 +01:00
const resolutions = resolveModuleNamesReusingOldState ( module Names , getNormalizedAbsolutePath ( file . fileName , currentDirectory ) , file ) ;
2016-10-13 22:02:22 +02:00
Debug . assert ( resolutions . length === module Names.length ) ;
2016-01-12 07:34:38 +01:00
for ( let i = 0 ; i < module Names.length ; i ++ ) {
2015-11-04 23:02:33 +01:00
const resolution = resolutions [ i ] ;
2015-09-10 20:36:31 +02:00
setResolvedModule ( file , module Names [ i ] , resolution ) ;
2016-06-27 05:48:22 +02:00
2016-10-13 22:02:22 +02:00
if ( ! resolution ) {
continue ;
}
const isFromNodeModulesSearch = resolution . isExternalLibraryImport ;
2016-10-25 21:38:59 +02:00
const isJsFileFromNodeModules = isFromNodeModulesSearch && ! extensionIsTypeScript ( resolution . extension ) ;
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.
2016-10-18 23:22:43 +02:00
const shouldAddFile = resolvedFileName && ! getResolutionDiagnostic ( options , resolution ) && ! options . noResolve && i < file . imports . length && ! elideImport ;
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 ) {
2016-10-13 22:02:22 +02:00
const path = toPath ( resolvedFileName , currentDirectory , getCanonicalFileName ) ;
const pos = skipTrivia ( file . text , file . imports [ i ] . pos ) ;
2016-10-19 18:20:45 +02:00
findSourceFile ( resolvedFileName , path , /*isDefaultLib*/ false , file , pos , file . imports [ i ] . end ) ;
2016-06-21 22:42:02 +02:00
}
2015-12-22 22:21:51 +01:00
2016-07-11 01:11:42 +02:00
if ( isFromNodeModulesSearch ) {
currentNodeModulesDepth -- ;
2015-08-04 02:42:29 +02:00
}
2015-08-19 20:58:02 +02:00
}
2015-06-23 02:48:44 +02:00
}
else {
// no imports - drop cached module resolutions
file . resolvedModules = undefined ;
}
2014-12-16 22:14:14 +01:00
}
2016-02-23 21:48:31 +01:00
function computeCommonSourceDirectory ( sourceFiles : SourceFile [ ] ) : string {
const fileNames : string [ ] = [ ] ;
for ( const file of sourceFiles ) {
if ( ! file . isDeclarationFile ) {
fileNames . push ( file . fileName ) ;
}
}
2016-04-01 21:41:01 +02:00
return computeCommonSourceDirectoryOfFilenames ( fileNames , currentDirectory , getCanonicalFileName ) ;
2016-02-23 21:48:31 +01:00
}
2015-04-19 22:24:39 +02:00
function checkSourceFilesBelongToPath ( sourceFiles : SourceFile [ ] , rootDirectory : string ) : boolean {
2015-04-15 07:11:25 +02:00
let allFilesBelongToPath = true ;
2015-04-19 22:24:39 +02:00
if ( sourceFiles ) {
2015-11-04 23:02:33 +01:00
const absoluteRootDirectoryPath = host . getCanonicalFileName ( getNormalizedAbsolutePath ( rootDirectory , currentDirectory ) ) ;
2015-04-15 07:11:25 +02:00
2016-01-16 23:05:46 +01:00
for ( const sourceFile of sourceFiles ) {
2015-04-15 07:11:25 +02:00
if ( ! isDeclarationFile ( sourceFile ) ) {
2015-11-04 23:02:33 +01:00
const absoluteSourceFilePath = host . getCanonicalFileName ( getNormalizedAbsolutePath ( sourceFile . fileName , currentDirectory ) ) ;
2015-04-15 07:11:25 +02:00
if ( absoluteSourceFilePath . indexOf ( absoluteRootDirectoryPath ) !== 0 ) {
2015-09-10 19:46:39 +02:00
programDiagnostics . add ( createCompilerDiagnostic ( Diagnostics . File_0_is_not_under_rootDir_1_rootDir_is_expected_to_contain_all_source_files , sourceFile . fileName , options . rootDir ) ) ;
2015-04-15 07:11:25 +02:00
allFilesBelongToPath = false ;
}
}
}
2015-04-15 00:05:08 +02:00
}
2015-04-15 07:11:25 +02:00
return allFilesBelongToPath ;
2015-04-15 00:05:08 +02:00
}
2014-12-16 22:14:14 +01:00
function verifyCompilerOptions() {
2015-05-19 06:49:41 +02:00
if ( options . isolatedModules ) {
2015-03-31 04:33:15 +02:00
if ( options . declaration ) {
2015-09-10 19:46:39 +02:00
programDiagnostics . add ( createCompilerDiagnostic ( Diagnostics . Option_0_cannot_be_specified_with_option_1 , "declaration" , "isolatedModules" ) ) ;
2015-03-31 04:33:15 +02:00
}
if ( options . noEmitOnError ) {
2015-09-10 19:46:39 +02:00
programDiagnostics . add ( createCompilerDiagnostic ( Diagnostics . Option_0_cannot_be_specified_with_option_1 , "noEmitOnError" , "isolatedModules" ) ) ;
2015-03-31 04:33:15 +02:00
}
if ( options . out ) {
2015-09-10 19:46:39 +02:00
programDiagnostics . add ( createCompilerDiagnostic ( 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 ) {
2015-09-10 19:46:39 +02:00
programDiagnostics . add ( createCompilerDiagnostic ( 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 ) {
2015-09-10 19:46:39 +02:00
programDiagnostics . add ( createCompilerDiagnostic ( Diagnostics . Option_0_cannot_be_specified_with_option_1 , "sourceMap" , "inlineSourceMap" ) ) ;
2015-04-08 09:14:23 +02:00
}
if ( options . mapRoot ) {
2015-09-10 19:46:39 +02:00
programDiagnostics . add ( createCompilerDiagnostic ( Diagnostics . Option_0_cannot_be_specified_with_option_1 , "mapRoot" , "inlineSourceMap" ) ) ;
2015-04-08 09:14:23 +02:00
}
2015-04-21 05:33:31 +02:00
}
2016-01-25 20:49:26 +01:00
if ( options . paths && options . baseUrl === undefined ) {
programDiagnostics . add ( createCompilerDiagnostic ( Diagnostics . Option_paths_cannot_be_used_without_specifying_baseUrl_option ) ) ;
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 ) ) {
programDiagnostics . add ( createCompilerDiagnostic ( Diagnostics . Pattern_0_can_have_at_most_one_Asterisk_character , key ) ) ;
}
2016-04-14 06:16:39 +02:00
if ( isArray ( options . paths [ key ] ) ) {
2016-07-23 08:05:36 +02:00
if ( options . paths [ key ] . length === 0 ) {
programDiagnostics . add ( createCompilerDiagnostic ( Diagnostics . Substitutions_for_pattern_0_shouldn_t_be_an_empty_array , key ) ) ;
}
2016-04-14 06:16:39 +02:00
for ( const subst of options . paths [ key ] ) {
const typeOfSubst = typeof subst ;
if ( typeOfSubst === "string" ) {
if ( ! hasZeroOrOneAsteriskCharacter ( subst ) ) {
programDiagnostics . add ( createCompilerDiagnostic ( Diagnostics . Substitution_0_in_pattern_1_in_can_have_at_most_one_Asterisk_character , subst , key ) ) ;
}
}
else {
programDiagnostics . add ( createCompilerDiagnostic ( Diagnostics . Substitution_0_for_pattern_1_has_incorrect_type_expected_string_got_2 , subst , key , typeOfSubst ) ) ;
}
2015-11-19 06:46:45 +01:00
}
}
2016-04-14 06:16:39 +02:00
else {
2016-05-19 02:12:59 +02:00
programDiagnostics . add ( createCompilerDiagnostic ( 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 ) {
programDiagnostics . add ( createCompilerDiagnostic ( 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-06-09 20:51:53 +02:00
programDiagnostics . add ( createCompilerDiagnostic ( 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 ) {
2015-09-10 19:46:39 +02:00
programDiagnostics . add ( createCompilerDiagnostic ( Diagnostics . Option_0_cannot_be_specified_with_option_1 , "out" , "outFile" ) ) ;
2015-08-21 02:37:56 +02:00
}
2016-06-09 20:51:53 +02:00
if ( options . mapRoot && ! options . sourceMap ) {
// Error to specify --mapRoot without --sourcemap
programDiagnostics . add ( createCompilerDiagnostic ( Diagnostics . Option_0_cannot_be_specified_without_specifying_option_1 , "mapRoot" , "sourceMap" ) ) ;
2014-12-16 22:14:14 +01:00
}
2016-02-23 21:12:52 +01:00
if ( options . declarationDir ) {
if ( ! options . declaration ) {
programDiagnostics . add ( createCompilerDiagnostic ( Diagnostics . Option_0_cannot_be_specified_without_specifying_option_1 , "declarationDir" , "declaration" ) ) ;
}
if ( options . out || options . outFile ) {
programDiagnostics . add ( createCompilerDiagnostic ( Diagnostics . Option_0_cannot_be_specified_with_option_1 , "declarationDir" , options . out ? "out" : "outFile" ) ) ;
}
2016-02-20 23:40:39 +01:00
}
2016-03-28 23:20:29 +02:00
if ( options . lib && options . noLib ) {
programDiagnostics . add ( createCompilerDiagnostic ( Diagnostics . Option_0_cannot_be_specified_with_option_1 , "lib" , "noLib" ) ) ;
}
2017-03-03 19:25:10 +01:00
if ( options . noImplicitUseStrict && ( options . alwaysStrict === undefined ? options.strict : options.alwaysStrict ) ) {
2016-10-14 01:54:09 +02:00
programDiagnostics . add ( createCompilerDiagnostic ( Diagnostics . Option_0_cannot_be_specified_with_option_1 , "noImplicitUseStrict" , "alwaysStrict" ) ) ;
}
2015-11-04 23:02:33 +01:00
const languageVersion = options . target || ScriptTarget . ES3 ;
const outFile = options . outFile || options . out ;
2015-02-13 02:54:30 +01:00
2016-07-23 00:41:52 +02:00
const firstNonAmbientExternalModuleSourceFile = forEach ( files , f = > isExternalModule ( f ) && ! isDeclarationFile ( f ) ? f : undefined ) ;
2015-05-19 06:49:41 +02:00
if ( options . isolatedModules ) {
2016-10-13 13:32:00 +02:00
if ( options . module === ModuleKind . None && languageVersion < ScriptTarget . ES2015 ) {
2016-02-11 20:01:10 +01:00
programDiagnostics . add ( createCompilerDiagnostic ( Diagnostics . Option_isolatedModules_can_only_be_used_when_either_option_module_is_provided_or_option_target_is_ES2015_or_higher ) ) ;
}
2015-11-04 23:02:33 +01:00
const firstNonExternalModuleSourceFile = forEach ( files , f = > ! isExternalModule ( f ) && ! isDeclarationFile ( f ) ? f : undefined ) ;
2015-03-31 04:33:15 +02:00
if ( firstNonExternalModuleSourceFile ) {
2015-11-04 23:02:33 +01:00
const span = getErrorSpanForNode ( firstNonExternalModuleSourceFile , firstNonExternalModuleSourceFile ) ;
2015-09-10 19:46:39 +02:00
programDiagnostics . add ( createFileDiagnostic ( firstNonExternalModuleSourceFile , span . start , span . length , Diagnostics . Cannot_compile_namespaces_when_the_isolatedModules_flag_is_provided ) ) ;
2015-03-31 04:33:15 +02:00
}
}
2016-10-13 13:32:00 +02:00
else if ( firstNonAmbientExternalModuleSourceFile && languageVersion < ScriptTarget . ES2015 && options . module === ModuleKind . None ) {
2016-02-11 20:01:10 +01:00
// We cannot use createDiagnosticFromNode because nodes do not have parents yet
2016-07-23 00:41:52 +02:00
const span = getErrorSpanForNode ( firstNonAmbientExternalModuleSourceFile , firstNonAmbientExternalModuleSourceFile . externalModuleIndicator ) ;
programDiagnostics . add ( createFileDiagnostic ( firstNonAmbientExternalModuleSourceFile , span . start , span . length , Diagnostics . Cannot_use_imports_exports_or_module_augmentations_when_module_is_none ) ) ;
2016-02-11 20:01:10 +01:00
}
2015-03-12 06:53:36 +01:00
2015-10-05 23:25:48 +02:00
// Cannot specify module gen that isn't amd or system with --out
2016-05-18 02:14:51 +02:00
if ( outFile ) {
if ( options . module && ! ( options . module === ModuleKind . AMD || options . module === ModuleKind . System ) ) {
programDiagnostics . add ( createCompilerDiagnostic ( Diagnostics . Only_amd_and_system_modules_are_supported_alongside_0 , options . out ? "out" : "outFile" ) ) ;
}
2016-07-23 00:41:52 +02:00
else if ( options . module === undefined && firstNonAmbientExternalModuleSourceFile ) {
const span = getErrorSpanForNode ( firstNonAmbientExternalModuleSourceFile , firstNonAmbientExternalModuleSourceFile . externalModuleIndicator ) ;
programDiagnostics . add ( createFileDiagnostic ( firstNonAmbientExternalModuleSourceFile , span . start , span . length , Diagnostics . Cannot_compile_modules_using_option_0_unless_the_module_flag_is_amd_or_system , options . out ? "out" : "outFile" ) ) ;
2016-05-18 02:14:51 +02:00
}
2015-10-03 02:03:29 +02:00
}
2015-10-22 00:27:33 +02:00
// there has to be common source directory if user specified --outdir || --sourceRoot
// if user specified --mapRoot, there needs to be common source directory if there would be multiple files being emitted
if ( options . outDir || // there is --outDir specified
options . sourceRoot || // there is --sourceRoot specified
options . mapRoot ) { // there is --mapRoot specified
2015-11-19 02:10:22 +01:00
// Precalculate and cache the common source directory
const dir = getCommonSourceDirectory ( ) ;
2015-10-22 00:27:33 +02:00
2015-11-19 02:10:22 +01:00
// If we failed to find a good common directory, but outDir is specified and at least one of our files is on a windows drive/URL/other resource, add a failure
if ( options . outDir && dir === "" && forEach ( files , file = > getRootLength ( file . fileName ) > 1 ) ) {
2016-02-23 21:48:31 +01:00
programDiagnostics . add ( createCompilerDiagnostic ( Diagnostics . Cannot_find_the_common_subdirectory_path_for_the_input_files ) ) ;
2015-11-18 22:19:56 +01:00
}
2015-10-22 00:27:33 +02:00
}
2016-03-12 21:14:00 +01:00
if ( ! options . noEmit && options . allowJs && options . declaration ) {
2015-10-30 19:50:07 +01:00
programDiagnostics . add ( createCompilerDiagnostic ( Diagnostics . Option_0_cannot_be_specified_with_option_1 , "allowJs" , "declaration" ) ) ;
}
2015-06-26 01:24:41 +02:00
2017-03-07 22:49:52 +01:00
if ( options . checkJs && ! options . allowJs ) {
programDiagnostics . add ( createCompilerDiagnostic ( Diagnostics . Option_0_cannot_be_specified_without_specifying_option_1 , "checkJs" , "allowJs" ) ) ;
}
2015-06-02 00:01:24 +02:00
if ( options . emitDecoratorMetadata &&
! options . experimentalDecorators ) {
2015-09-10 19:46:39 +02:00
programDiagnostics . add ( createCompilerDiagnostic ( 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 ) {
programDiagnostics . add ( createCompilerDiagnostic ( Diagnostics . Option_0_cannot_be_specified_with_option_1 , "reactNamespace" , "jsxFactory" ) ) ;
}
2016-11-10 17:53:32 +01:00
if ( ! parseIsolatedEntityName ( options . jsxFactory , languageVersion ) ) {
2016-11-09 22:15:13 +01:00
programDiagnostics . add ( createCompilerDiagnostic ( Diagnostics . Invalid_value_for_jsxFactory_0_is_not_a_valid_identifier_or_qualified_name , options . jsxFactory ) ) ;
}
2016-11-09 21:12:48 +01:00
}
else if ( options . reactNamespace && ! isIdentifierText ( options . reactNamespace , languageVersion ) ) {
2016-01-30 20:37:02 +01:00
programDiagnostics . add ( createCompilerDiagnostic ( Diagnostics . Invalid_value_for_reactNamespace_0_is_not_a_valid_identifier , options . reactNamespace ) ) ;
2016-01-08 00:00:50 +01:00
}
2015-11-06 05:09:40 +01:00
// If the emit is enabled make sure that every output file is unique and not overwriting any of the input files
2016-02-01 19:36:47 +01:00
if ( ! options . noEmit && ! options . suppressOutputPathCheck ) {
2015-11-06 21:39:42 +01:00
const emitHost = getEmitHost ( ) ;
const emitFilesSeen = createFileMap < boolean > ( ! host . useCaseSensitiveFileNames ( ) ? key = > key . toLocaleLowerCase ( ) : undefined ) ;
2017-01-23 20:14:29 +01:00
forEachEmittedFile ( emitHost , ( emitFileNames ) = > {
2015-10-30 21:22:23 +01:00
verifyEmitFilePath ( emitFileNames . jsFilePath , emitFilesSeen ) ;
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
2015-10-30 23:54:31 +01:00
function verifyEmitFilePath ( emitFileName : string , emitFilesSeen : FileMap < boolean > ) {
if ( emitFileName ) {
2015-11-06 21:39:42 +01:00
const emitFilePath = toPath ( emitFileName , currentDirectory , getCanonicalFileName ) ;
2015-10-12 21:25:13 +02:00
// Report error if the output overwrites input file
2015-11-18 19:48:03 +01:00
if ( filesByName . contains ( emitFilePath ) ) {
2016-11-10 23:12:24 +01:00
let chain : DiagnosticMessageChain ;
if ( ! options . configFilePath ) {
// The program is from either an inferred project or an external project
chain = chainDiagnosticMessages ( /*details*/ undefined , Diagnostics . Adding_a_tsconfig_json_file_will_help_organize_projects_that_contain_both_TypeScript_and_JavaScript_files_Learn_more_at_https_Colon_Slash_Slashaka_ms_Slashtsconfig ) ;
2016-11-04 05:13:41 +01:00
}
2016-11-10 23:12:24 +01:00
chain = chainDiagnosticMessages ( chain , Diagnostics . Cannot_write_file_0_because_it_would_overwrite_input_file , emitFileName ) ;
blockEmittingOfFile ( emitFileName , createCompilerDiagnosticFromMessageChain ( chain ) ) ;
2015-10-12 21:25:13 +02:00
}
2015-10-30 21:22:23 +01:00
// Report error if multiple files write into same file
2015-10-30 23:54:31 +01:00
if ( emitFilesSeen . contains ( emitFilePath ) ) {
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 {
2015-10-30 23:54:31 +01:00
emitFilesSeen . set ( emitFilePath , true ) ;
2015-10-12 21:25:13 +02:00
}
2015-10-30 21:22:23 +01:00
}
2015-10-12 21:25:13 +02:00
}
}
2016-11-10 23:12:24 +01:00
function blockEmittingOfFile ( emitFileName : string , diag : Diagnostic ) {
2015-10-30 23:54:31 +01:00
hasEmitBlockingDiagnostics . set ( toPath ( emitFileName , currentDirectory , getCanonicalFileName ) , true ) ;
2016-11-10 23:12:24 +01:00
programDiagnostics . add ( diag ) ;
2014-12-16 22:14:14 +01:00
}
}
2016-10-13 22:02:22 +02:00
/* @internal */
/ * *
2016-10-27 16:19:37 +02:00
* Returns a DiagnosticMessage if we won ' t include a resolved module due to its extension .
2016-10-13 22:02:22 +02:00
* The DiagnosticMessage ' s parameters are the imported module name , and the filename it resolved to .
2016-10-27 16:19:37 +02:00
* This returns a diagnostic even if the module will be an untyped module .
2016-10-13 22:02:22 +02:00
* /
2016-10-27 20:33:01 +02:00
export function getResolutionDiagnostic ( options : CompilerOptions , { extension } : ResolvedModuleFull ) : DiagnosticMessage | undefined {
2016-10-25 21:38:59 +02:00
switch ( extension ) {
case Extension . Ts :
case Extension . Dts :
// These are always allowed.
2016-10-18 23:22:43 +02:00
return undefined ;
2016-10-25 21:38:59 +02:00
case Extension . Tsx :
2016-10-27 17:08:02 +02:00
return needJsx ( ) ;
2016-10-25 21:38:59 +02:00
case Extension . Jsx :
2016-10-27 17:08:02 +02:00
return needJsx ( ) || needAllowJs ( ) ;
2016-10-25 21:38:59 +02:00
case Extension . Js :
2016-10-27 17:08:02 +02:00
return needAllowJs ( ) ;
}
function needJsx() {
return options . jsx ? undefined : Diagnostics . Module_0_was_resolved_to_1_but_jsx_is_not_set ;
}
function needAllowJs() {
return options . allowJs ? undefined : Diagnostics . Module_0_was_resolved_to_1_but_allowJs_is_not_set ;
2016-10-13 22:02:22 +02:00
}
}
2015-06-17 20:08:13 +02:00
}