@@ -210,6 +210,7 @@ namespace ts {
210
210
originalWriteFile : CompilerHost [ "writeFile" ] | undefined ;
211
211
originalReadFileWithCache : CompilerHost [ "readFile" ] ;
212
212
originalGetSourceFile : CompilerHost [ "getSourceFile" ] ;
213
+ buildInfoCache : ESMap < Path , BuildInfo | false > ;
213
214
}
214
215
215
216
interface FileWatcherWithModifiedTime {
@@ -282,7 +283,7 @@ namespace ts {
282
283
283
284
// State of the solution
284
285
const baseCompilerOptions = getCompilerOptionsOfBuildOptions ( options ) ;
285
- const compilerHost = createCompilerHostFromProgramHost ( host , ( ) => state . projectCompilerOptions ) ;
286
+ const compilerHost = createCompilerHostFromProgramHost ( host , ( ) => state . projectCompilerOptions ) as CompilerHost & ReadBuildProgramHost ;
286
287
setGetSourceFileAsHashVersioned ( compilerHost , host ) ;
287
288
compilerHost . getParsedCommandLine = fileName => parseConfigFile ( state , fileName as ResolvedConfigFileName , toResolvedConfigFilePath ( state , fileName as ResolvedConfigFileName ) ) ;
288
289
compilerHost . resolveModuleNames = maybeBind ( host , host . resolveModuleNames ) ;
@@ -300,6 +301,7 @@ namespace ts {
300
301
compilerHost . resolveTypeReferenceDirectives = ( typeReferenceDirectiveNames , containingFile , redirectedReference , _options , containingFileMode ) =>
301
302
loadWithTypeDirectiveCache < ResolvedTypeReferenceDirective > ( Debug . checkEachDefined ( typeReferenceDirectiveNames ) , containingFile , redirectedReference , containingFileMode , loader ) ;
302
303
}
304
+ compilerHost . getBuildInfo = fileName => getBuildInfo ( state , fileName ) ;
303
305
304
306
const { watchFile, watchDirectory, writeLog } = createWatchFactory < ResolvedConfigFileName > ( hostWithWatch , options ) ;
305
307
@@ -573,6 +575,7 @@ namespace ts {
573
575
originalWriteFile,
574
576
originalReadFileWithCache,
575
577
originalGetSourceFile,
578
+ buildInfoCache : new Map ( ) ,
576
579
} ;
577
580
}
578
581
@@ -986,7 +989,9 @@ namespace ts {
986
989
}
987
990
}
988
991
989
- emittedOutputs . set ( toPath ( state , name ) , name ) ;
992
+ const path = toPath ( state , name ) ;
993
+ emittedOutputs . set ( path , name ) ;
994
+ state . cache ?. buildInfoCache . delete ( path ) ;
990
995
writeFile ( writeFileCallback ? { writeFile : writeFileCallback } : compilerHost , emitterDiagnostics , name , text , writeByteOrderMark ) ;
991
996
} ) ;
992
997
@@ -1003,7 +1008,12 @@ namespace ts {
1003
1008
function emitBuildInfo ( writeFileCallback ?: WriteFileCallback , cancellationToken ?: CancellationToken ) : EmitResult {
1004
1009
Debug . assertIsDefined ( program ) ;
1005
1010
Debug . assert ( step === BuildStep . EmitBuildInfo ) ;
1006
- const emitResult = program . emitBuildInfo ( writeFileCallback , cancellationToken ) ;
1011
+ const emitResult = program . emitBuildInfo ( ( name , data , writeByteOrderMark , onError , sourceFiles ) => {
1012
+ const path = toPath ( state , name ) ;
1013
+ state . cache ?. buildInfoCache . delete ( path ) ;
1014
+ if ( writeFileCallback ) writeFileCallback ( name , data , writeByteOrderMark , onError , sourceFiles ) ;
1015
+ else state . compilerHost . writeFile ( name , data , writeByteOrderMark , onError , sourceFiles ) ;
1016
+ } , cancellationToken ) ;
1007
1017
if ( emitResult . diagnostics . length ) {
1008
1018
reportErrors ( state , emitResult . diagnostics ) ;
1009
1019
state . diagnostics . set ( projectPath , [ ...state . diagnostics . get ( projectPath ) ! , ...emitResult . diagnostics ] ) ;
@@ -1101,7 +1111,9 @@ namespace ts {
1101
1111
const emitterDiagnostics = createDiagnosticCollection ( ) ;
1102
1112
const emittedOutputs = new Map < Path , string > ( ) ;
1103
1113
outputFiles . forEach ( ( { name, text, writeByteOrderMark } ) => {
1104
- emittedOutputs . set ( toPath ( state , name ) , name ) ;
1114
+ const path = toPath ( state , name ) ;
1115
+ emittedOutputs . set ( path , name ) ;
1116
+ state . cache ?. buildInfoCache . delete ( path ) ;
1105
1117
writeFile ( writeFileCallback ? { writeFile : writeFileCallback } : compilerHost , emitterDiagnostics , name , text , writeByteOrderMark ) ;
1106
1118
} ) ;
1107
1119
@@ -1390,6 +1402,16 @@ namespace ts {
1390
1402
} ;
1391
1403
}
1392
1404
1405
+ function getBuildInfo ( state : SolutionBuilderState , buildInfoPath : string ) : BuildInfo | undefined {
1406
+ const path = toPath ( state , buildInfoPath ) ;
1407
+ const existing = state . cache ?. buildInfoCache . get ( path ) ;
1408
+ if ( existing !== undefined ) return existing || undefined ;
1409
+ const value = state . readFileWithCache ( buildInfoPath ) ;
1410
+ const buildInfo = value ? ts . getBuildInfo ( value ) : undefined ;
1411
+ state . cache ?. buildInfoCache . set ( path , buildInfo || false ) ;
1412
+ return buildInfo ;
1413
+ }
1414
+
1393
1415
function checkConfigFileUpToDateStatus ( state : SolutionBuilderState , configFile : string , oldestOutputFileTime : Date , oldestOutputFileName : string ) : Status . OutOfDateWithSelf | undefined {
1394
1416
// Check tsconfig time
1395
1417
const tsconfigTime = getModifiedTime ( state , configFile ) ;
@@ -1472,43 +1494,89 @@ namespace ts {
1472
1494
1473
1495
if ( force ) return { type : UpToDateStatusType . ForceBuild } ;
1474
1496
1475
- // Collect the expected outputs of this project
1476
- const outputs = getAllProjectOutputs ( project , ! host . useCaseSensitiveFileNames ( ) ) ;
1477
-
1497
+ const buildInfoPath = getTsBuildInfoEmitOutputFilePath ( project . options ) ;
1478
1498
// Now see if all outputs are newer than the newest input
1479
1499
let oldestOutputFileName = "(none)" ;
1480
1500
let oldestOutputFileTime = maximumDate ;
1481
1501
let newestDeclarationFileContentChangedTime ;
1482
- for ( const output of outputs ) {
1483
- // Output is missing; can stop checking
1484
- const outputTime = ts . getModifiedTime ( host , output ) ;
1502
+ if ( buildInfoPath ) {
1503
+ const outputTime = ts . getModifiedTime ( host , buildInfoPath ) ;
1485
1504
if ( outputTime === missingFileModifiedTime ) {
1486
1505
return {
1487
1506
type : UpToDateStatusType . OutputMissing ,
1488
- missingOutputFileName : output
1507
+ missingOutputFileName : buildInfoPath
1489
1508
} ;
1490
1509
}
1491
1510
1492
- if ( outputTime < oldestOutputFileTime ) {
1493
- oldestOutputFileTime = outputTime ;
1494
- oldestOutputFileName = output ;
1511
+ const buildInfo = Debug . checkDefined ( getBuildInfo ( state , buildInfoPath ) ) ;
1512
+ if ( ! state . buildInfoChecked . has ( resolvedPath ) ) {
1513
+ state . buildInfoChecked . set ( resolvedPath , true ) ;
1514
+ if ( buildInfo && ( buildInfo . bundle || buildInfo . program ) && buildInfo . version !== version ) {
1515
+ return {
1516
+ type : UpToDateStatusType . TsVersionOutputOfDate ,
1517
+ version : buildInfo . version
1518
+ } ;
1519
+ }
1495
1520
}
1496
1521
1497
1522
// If an output is older than the newest input, we can stop checking
1498
1523
if ( outputTime < newestInputFileTime ) {
1499
1524
return {
1500
1525
type : UpToDateStatusType . OutOfDateWithSelf ,
1501
- outOfDateOutputFileName : oldestOutputFileName ,
1526
+ outOfDateOutputFileName : buildInfoPath ,
1502
1527
newerInputFileName : newestInputFileName
1503
1528
} ;
1504
1529
}
1505
1530
1506
- // Keep track of when the most recent time a .d.ts file was changed.
1507
- // In addition to file timestamps, we also keep track of when a .d.ts file
1508
- // had its file touched but not had its contents changed - this allows us
1509
- // to skip a downstream typecheck
1510
- if ( isDeclarationFileName ( output ) ) {
1511
- newestDeclarationFileContentChangedTime = newer ( newestDeclarationFileContentChangedTime , outputTime ) ;
1531
+ if ( buildInfo . program ) {
1532
+ if ( buildInfo . program . hasPendingChange ||
1533
+ ( ! buildInfo . program . options ?. noEmit && buildInfo . program . affectedFilesPendingEmit ?. length ) ) {
1534
+ return {
1535
+ type : UpToDateStatusType . OutOfDateBuildInfo ,
1536
+ buildInfoFile : buildInfoPath
1537
+ } ;
1538
+ }
1539
+ }
1540
+
1541
+ oldestOutputFileTime = outputTime ;
1542
+ oldestOutputFileName = buildInfoPath ;
1543
+ }
1544
+ // Dont check output timestamps if we have buildinfo telling us output is uptodate
1545
+ else {
1546
+ // Collect the expected outputs of this project
1547
+ const outputs = getAllProjectOutputs ( project , ! host . useCaseSensitiveFileNames ( ) ) ;
1548
+ for ( const output of outputs ) {
1549
+ if ( buildInfoPath === output ) continue ;
1550
+ // Output is missing; can stop checking
1551
+ const outputTime = ts . getModifiedTime ( state . host , output ) ;
1552
+ if ( outputTime === missingFileModifiedTime ) {
1553
+ return {
1554
+ type : UpToDateStatusType . OutputMissing ,
1555
+ missingOutputFileName : output
1556
+ } ;
1557
+ }
1558
+
1559
+ if ( outputTime < oldestOutputFileTime ) {
1560
+ oldestOutputFileTime = outputTime ;
1561
+ oldestOutputFileName = output ;
1562
+ }
1563
+
1564
+ // If an output is older than the newest input, we can stop checking
1565
+ if ( outputTime < newestInputFileTime ) {
1566
+ return {
1567
+ type : UpToDateStatusType . OutOfDateWithSelf ,
1568
+ outOfDateOutputFileName : oldestOutputFileName ,
1569
+ newerInputFileName : newestInputFileName
1570
+ } ;
1571
+ }
1572
+
1573
+ // Keep track of when the most recent time a .d.ts file was changed.
1574
+ // In addition to file timestamps, we also keep track of when a .d.ts file
1575
+ // had its file touched but not had its contents changed - this allows us
1576
+ // to skip a downstream typecheck
1577
+ if ( isDeclarationFileName ( output ) ) {
1578
+ newestDeclarationFileContentChangedTime = newer ( newestDeclarationFileContentChangedTime , outputTime ) ;
1579
+ }
1512
1580
}
1513
1581
}
1514
1582
@@ -1557,21 +1625,6 @@ namespace ts {
1557
1625
) ;
1558
1626
if ( dependentPackageFileStatus ) return dependentPackageFileStatus ;
1559
1627
1560
- if ( ! state . buildInfoChecked . has ( resolvedPath ) ) {
1561
- state . buildInfoChecked . set ( resolvedPath , true ) ;
1562
- const buildInfoPath = getTsBuildInfoEmitOutputFilePath ( project . options ) ;
1563
- if ( buildInfoPath ) {
1564
- const value = state . readFileWithCache ( buildInfoPath ) ;
1565
- const buildInfo = value && getBuildInfo ( value ) ;
1566
- if ( buildInfo && ( buildInfo . bundle || buildInfo . program ) && buildInfo . version !== version ) {
1567
- return {
1568
- type : UpToDateStatusType . TsVersionOutputOfDate ,
1569
- version : buildInfo . version
1570
- } ;
1571
- }
1572
- }
1573
- }
1574
-
1575
1628
if ( usesPrepend && pseudoUpToDate ) {
1576
1629
return {
1577
1630
type : UpToDateStatusType . OutOfDateWithPrepend ,
@@ -2103,6 +2156,13 @@ namespace ts {
2103
2156
relName ( state , configFileName ) ,
2104
2157
relName ( state , status . missingOutputFileName )
2105
2158
) ;
2159
+ case UpToDateStatusType . OutOfDateBuildInfo :
2160
+ return reportStatus (
2161
+ state ,
2162
+ Diagnostics . Project_0_is_out_of_date_because_buildinfo_file_1_indicates_that_some_of_the_changes_are_not_emitted ,
2163
+ relName ( state , configFileName ) ,
2164
+ relName ( state , status . buildInfoFile )
2165
+ ) ;
2106
2166
case UpToDateStatusType . UpToDate :
2107
2167
if ( status . newestInputFileTime !== undefined ) {
2108
2168
return reportStatus (
0 commit comments