@@ -72,6 +72,11 @@ namespace ts {
72
72
return date1 ? date2 > date1 ? date2 : date1 : date2 ;
73
73
}
74
74
75
+ /*@internal */
76
+ export function getCurrentTime ( host : { now ?( ) : Date ; } ) {
77
+ return host . now ? host . now ( ) : new Date ( ) ;
78
+ }
79
+
75
80
export type ReportEmitErrorSummary = ( errorCount : number , filesInError : ( ReportFileInError | undefined ) [ ] ) => void ;
76
81
77
82
export interface ReportFileInError {
@@ -985,12 +990,12 @@ namespace ts {
985
990
let newestDeclarationFileContentChangedTime : Date | undefined ;
986
991
const emitterDiagnostics = createDiagnosticCollection ( ) ;
987
992
const emittedOutputs = new Map < Path , string > ( ) ;
988
- const buildInfoEntry = state . buildInfoCache . get ( projectPath ) ;
993
+ const isOutFile = outFile ( config . options ) ;
989
994
outputFiles . forEach ( ( { name, text, writeByteOrderMark, buildInfo } ) => {
990
995
if ( resultFlags === BuildResultFlags . DeclarationOutputUnchanged && isDeclarationFileName ( name ) ) {
991
996
// Check for unchanged .d.ts files
992
997
if ( state . readFileWithCache ( name ) === text ) {
993
- if ( config . options . composite ) {
998
+ if ( config . options . composite && isOutFile ) {
994
999
newestDeclarationFileContentChangedTime = newer ( newestDeclarationFileContentChangedTime , ts . getModifiedTime ( host , name ) ) ;
995
1000
}
996
1001
}
@@ -1001,10 +1006,7 @@ namespace ts {
1001
1006
1002
1007
const path = toPath ( state , name ) ;
1003
1008
emittedOutputs . set ( path , name ) ;
1004
- if ( buildInfoEntry ?. path === path ) {
1005
- buildInfoEntry . buildInfo = buildInfo ! ;
1006
- buildInfoEntry . modifiedTime = getCurrentTime ( state ) ;
1007
- }
1009
+ if ( buildInfo ) setBuildInfo ( state , buildInfo , projectPath , program ! . getCompilerOptions ( ) ) ;
1008
1010
writeFile ( writeFileCallback ? { writeFile : writeFileCallback } : compilerHost , emitterDiagnostics , name , text , writeByteOrderMark ) ;
1009
1011
} ) ;
1010
1012
@@ -1022,12 +1024,7 @@ namespace ts {
1022
1024
Debug . assertIsDefined ( program ) ;
1023
1025
Debug . assert ( step === BuildStep . EmitBuildInfo ) ;
1024
1026
const emitResult = program . emitBuildInfo ( ( name , text , writeByteOrderMark , onError , sourceFiles , data ) => {
1025
- const path = toPath ( state , name ) ;
1026
- const buildInfo = state . buildInfoCache . get ( projectPath ) ;
1027
- if ( buildInfo ?. path === path ) {
1028
- buildInfo . buildInfo = data ! . buildInfo ! ;
1029
- buildInfo . modifiedTime = getCurrentTime ( state ) ;
1030
- }
1027
+ if ( data ?. buildInfo ) setBuildInfo ( state , data . buildInfo , projectPath , program ! . getCompilerOptions ( ) ) ;
1031
1028
if ( writeFileCallback ) writeFileCallback ( name , text , writeByteOrderMark , onError , sourceFiles , data ) ;
1032
1029
else state . compilerHost . writeFile ( name , text , writeByteOrderMark , onError , sourceFiles , data ) ;
1033
1030
} , cancellationToken ) ;
@@ -1076,7 +1073,7 @@ namespace ts {
1076
1073
state . diagnostics . delete ( projectPath ) ;
1077
1074
state . projectStatus . set ( projectPath , {
1078
1075
type : UpToDateStatusType . UpToDate ,
1079
- newestDeclarationFileContentChangedTime : anyDtsChange ? undefined : newestDeclarationFileContentChangedTime ,
1076
+ newestDeclarationFileContentChangedTime : newestDeclarationFileContentChangedTime || getDtsChangeTime ( state , config . options , projectPath ) ,
1080
1077
oldestOutputFileName
1081
1078
} ) ;
1082
1079
afterProgramDone ( state , program , config ) ;
@@ -1127,14 +1124,10 @@ namespace ts {
1127
1124
Debug . assert ( ! ! outputFiles . length ) ;
1128
1125
const emitterDiagnostics = createDiagnosticCollection ( ) ;
1129
1126
const emittedOutputs = new Map < Path , string > ( ) ;
1130
- const buildInfoEntry = state . buildInfoCache . get ( projectPath ) ;
1131
1127
outputFiles . forEach ( ( { name, text, writeByteOrderMark, buildInfo } ) => {
1132
1128
const path = toPath ( state , name ) ;
1133
1129
emittedOutputs . set ( path , name ) ;
1134
- if ( buildInfoEntry ?. path === path ) {
1135
- buildInfoEntry . buildInfo = buildInfo ! ;
1136
- buildInfoEntry . modifiedTime = getCurrentTime ( state ) ;
1137
- }
1130
+ if ( buildInfo ) setBuildInfo ( state , buildInfo , projectPath , config . options ) ;
1138
1131
writeFile ( writeFileCallback ? { writeFile : writeFileCallback } : compilerHost , emitterDiagnostics , name , text , writeByteOrderMark ) ;
1139
1132
} ) ;
1140
1133
@@ -1423,6 +1416,18 @@ namespace ts {
1423
1416
} ;
1424
1417
}
1425
1418
1419
+ function setBuildInfo ( state : SolutionBuilderState , buildInfo : BuildInfo , resolvedConfigPath : ResolvedConfigFilePath , options : CompilerOptions ) {
1420
+ const buildInfoPath = getTsBuildInfoEmitOutputFilePath ( options ) ! ;
1421
+ const existing = getBuildInfoCacheEntry ( state , buildInfoPath , resolvedConfigPath ) ;
1422
+ if ( existing ) {
1423
+ existing . buildInfo = buildInfo ;
1424
+ existing . modifiedTime = getCurrentTime ( state . host ) ;
1425
+ }
1426
+ else {
1427
+ state . buildInfoCache . set ( resolvedConfigPath , { path : toPath ( state , buildInfoPath ) , buildInfo, modifiedTime : getCurrentTime ( state . host ) } ) ;
1428
+ }
1429
+ }
1430
+
1426
1431
function getBuildInfoCacheEntry ( state : SolutionBuilderState , buildInfoPath : string , resolvedConfigPath : ResolvedConfigFilePath ) {
1427
1432
const path = toPath ( state , buildInfoPath ) ;
1428
1433
const existing = state . buildInfoCache . get ( resolvedConfigPath ) ;
@@ -1470,7 +1475,8 @@ namespace ts {
1470
1475
for ( const ref of project . projectReferences ) {
1471
1476
const resolvedRef = resolveProjectReferencePath ( ref ) ;
1472
1477
const resolvedRefPath = toResolvedConfigFilePath ( state , resolvedRef ) ;
1473
- const refStatus = getUpToDateStatus ( state , parseConfigFile ( state , resolvedRef , resolvedRefPath ) , resolvedRefPath ) ;
1478
+ const resolvedConfig = parseConfigFile ( state , resolvedRef , resolvedRefPath ) ! ;
1479
+ const refStatus = getUpToDateStatus ( state , resolvedConfig , resolvedRefPath ) ;
1474
1480
1475
1481
// Its a circular reference ignore the status of this project
1476
1482
if ( refStatus . type === UpToDateStatusType . ComputingUpstream ||
@@ -1496,7 +1502,7 @@ namespace ts {
1496
1502
} ;
1497
1503
}
1498
1504
1499
- if ( ! force ) ( referenceStatuses ||= [ ] ) . push ( { ref, refStatus } ) ;
1505
+ if ( ! force ) ( referenceStatuses ||= [ ] ) . push ( { ref, refStatus, resolvedRefPath , resolvedConfig } ) ;
1500
1506
}
1501
1507
}
1502
1508
if ( force ) return { type : UpToDateStatusType . ForceBuild } ;
@@ -1507,6 +1513,7 @@ namespace ts {
1507
1513
let oldestOutputFileName = "(none)" ;
1508
1514
let oldestOutputFileTime = maximumDate ;
1509
1515
let buildInfoTime : Date | undefined ;
1516
+ let newestDeclarationFileContentChangedTime ;
1510
1517
if ( buildInfoPath ) {
1511
1518
const buildInfoCacheEntry = getBuildInfoCacheEntry ( state , buildInfoPath , resolvedPath ) ;
1512
1519
buildInfoTime = buildInfoCacheEntry ?. modifiedTime || ts . getModifiedTime ( host , buildInfoPath ) ;
@@ -1540,6 +1547,7 @@ namespace ts {
1540
1547
1541
1548
oldestOutputFileTime = buildInfoTime ;
1542
1549
oldestOutputFileName = buildInfoPath ;
1550
+ newestDeclarationFileContentChangedTime = buildInfo . program ?. dtsChangeTime ? new Date ( buildInfo . program . dtsChangeTime ) : undefined ;
1543
1551
}
1544
1552
1545
1553
// Check input files
@@ -1601,18 +1609,31 @@ namespace ts {
1601
1609
}
1602
1610
}
1603
1611
1612
+ const seenRefs = buildInfoPath ? new Set < ResolvedConfigFilePath > ( ) : undefined ;
1613
+ const buildInfoCacheEntry = state . buildInfoCache . get ( resolvedPath ) ! ;
1614
+ seenRefs ?. add ( resolvedPath ) ;
1615
+
1604
1616
let pseudoUpToDate = false ;
1605
1617
let usesPrepend = false ;
1606
1618
let upstreamChangedProject : string | undefined ;
1607
1619
if ( referenceStatuses ) {
1608
- for ( const { ref, refStatus } of referenceStatuses ) {
1620
+ for ( const { ref, refStatus, resolvedConfig , resolvedRefPath } of referenceStatuses ) {
1609
1621
usesPrepend = usesPrepend || ! ! ( ref . prepend ) ;
1610
1622
// If the upstream project's newest file is older than our oldest output, we
1611
1623
// can't be out of date because of it
1612
1624
if ( refStatus . newestInputFileTime && refStatus . newestInputFileTime <= oldestOutputFileTime ) {
1613
1625
continue ;
1614
1626
}
1615
1627
1628
+ // Check if tsbuildinfo path is shared, then we need to rebuild
1629
+ if ( buildInfoCacheEntry && hasSameBuildInfo ( state , buildInfoCacheEntry , seenRefs ! , resolvedConfig , resolvedRefPath ) ) {
1630
+ return {
1631
+ type : UpToDateStatusType . OutOfDateWithUpstream ,
1632
+ outOfDateOutputFileName : buildInfoPath ! ,
1633
+ newerProjectName : ref . path
1634
+ } ;
1635
+ }
1636
+
1616
1637
// If the upstream project has only change .d.ts files, and we've built
1617
1638
// *after* those files, then we're "psuedo up to date" and eligible for a fast rebuild
1618
1639
if ( refStatus . newestDeclarationFileContentChangedTime && refStatus . newestDeclarationFileContentChangedTime <= oldestOutputFileTime ) {
@@ -1657,13 +1678,31 @@ namespace ts {
1657
1678
// Up to date
1658
1679
return {
1659
1680
type : pseudoUpToDate ? UpToDateStatusType . UpToDateWithUpstreamTypes : UpToDateStatusType . UpToDate ,
1660
- newestDeclarationFileContentChangedTime : undefined ,
1681
+ newestDeclarationFileContentChangedTime,
1661
1682
newestInputFileTime,
1662
1683
newestInputFileName,
1663
1684
oldestOutputFileName
1664
1685
} ;
1665
1686
}
1666
1687
1688
+ function hasSameBuildInfo ( state : SolutionBuilderState , buildInfoCacheEntry : BuildInfoCacheEntry , seenRefs : Set < ResolvedConfigFilePath > , resolvedConfig : ParsedCommandLine , resolvedRefPath : ResolvedConfigFilePath ) {
1689
+ if ( seenRefs . has ( resolvedRefPath ) ) return false ;
1690
+ seenRefs . add ( resolvedRefPath ) ;
1691
+ const refBuildInfo = state . buildInfoCache . get ( resolvedRefPath ) ! ;
1692
+ if ( refBuildInfo . path === buildInfoCacheEntry . path ) return true ;
1693
+
1694
+ if ( resolvedConfig . projectReferences ) {
1695
+ // Check references
1696
+ for ( const ref of resolvedConfig . projectReferences ) {
1697
+ const resolvedRef = resolveProjectReferencePath ( ref ) ;
1698
+ const resolvedRefPath = toResolvedConfigFilePath ( state , resolvedRef ) ;
1699
+ const resolvedConfig = parseConfigFile ( state , resolvedRef , resolvedRefPath ) ! ;
1700
+ if ( hasSameBuildInfo ( state , buildInfoCacheEntry , seenRefs , resolvedConfig , resolvedRefPath ) ) return true ;
1701
+ }
1702
+ }
1703
+ return false ;
1704
+ }
1705
+
1667
1706
function getUpToDateStatus ( state : SolutionBuilderState , project : ParsedCommandLine | undefined , resolvedPath : ResolvedConfigFilePath ) : UpToDateStatus {
1668
1707
if ( project === undefined ) {
1669
1708
return { type : UpToDateStatusType . Unbuildable , reason : "File deleted mid-build" } ;
@@ -1679,16 +1718,13 @@ namespace ts {
1679
1718
return actual ;
1680
1719
}
1681
1720
1682
- function getCurrentTime ( state : SolutionBuilderState ) {
1683
- return state . host . now ? state . host . now ( ) : new Date ( ) ;
1684
- }
1685
-
1686
1721
function updateOutputTimestampsWorker ( state : SolutionBuilderState , proj : ParsedCommandLine , anyDtsChange : boolean , verboseMessage : DiagnosticMessage , newestDeclarationFileContentChangedTime ?: Date , skipOutputs ?: ESMap < Path , string > ) {
1687
1722
if ( proj . options . noEmit ) return undefined ;
1688
1723
1689
1724
const buildInfoPath = getTsBuildInfoEmitOutputFilePath ( proj . options ) ;
1690
1725
const { host } = state ;
1691
1726
const outputs = getAllProjectOutputs ( proj , ! host . useCaseSensitiveFileNames ( ) ) ;
1727
+ const isOutFile = outFile ( proj . options ) ;
1692
1728
if ( ! skipOutputs || outputs . length !== skipOutputs . size ) {
1693
1729
let reportVerbose = ! ! state . options . verbose ;
1694
1730
let now : Date | undefined ;
@@ -1697,7 +1733,7 @@ namespace ts {
1697
1733
continue ;
1698
1734
}
1699
1735
1700
- if ( proj . options . composite && ! anyDtsChange && isDeclarationFileName ( file ) ) {
1736
+ if ( proj . options . composite && isOutFile && ! anyDtsChange && isDeclarationFileName ( file ) ) {
1701
1737
newestDeclarationFileContentChangedTime = newer ( newestDeclarationFileContentChangedTime , ts . getModifiedTime ( host , file ) ) ;
1702
1738
}
1703
1739
@@ -1707,22 +1743,29 @@ namespace ts {
1707
1743
reportStatus ( state , verboseMessage , proj . options . configFilePath ! ) ;
1708
1744
}
1709
1745
1710
- host . setModifiedTime ( file , now ||= getCurrentTime ( state ) ) ;
1746
+ host . setModifiedTime ( file , now ||= getCurrentTime ( state . host ) ) ;
1711
1747
}
1712
1748
}
1713
1749
}
1714
1750
1715
1751
return newestDeclarationFileContentChangedTime ;
1716
1752
}
1717
1753
1754
+ function getDtsChangeTime ( state : SolutionBuilderState , options : CompilerOptions , resolvedConfigPath : ResolvedConfigFilePath ) {
1755
+ if ( ! options . composite || outFile ( options ) ) return undefined ;
1756
+ const buildInfoPath = getTsBuildInfoEmitOutputFilePath ( options ) ! ;
1757
+ const buildInfo = getBuildInfo ( state , buildInfoPath , resolvedConfigPath , /*modifiedTime*/ undefined ) ;
1758
+ return buildInfo ?. program ?. dtsChangeTime ? new Date ( buildInfo . program . dtsChangeTime ) : undefined ;
1759
+ }
1760
+
1718
1761
function updateOutputTimestamps ( state : SolutionBuilderState , proj : ParsedCommandLine , resolvedPath : ResolvedConfigFilePath ) {
1719
1762
if ( state . options . dry ) {
1720
1763
return reportStatus ( state , Diagnostics . A_non_dry_build_would_update_timestamps_for_output_of_project_0 , proj . options . configFilePath ! ) ;
1721
1764
}
1722
1765
const priorNewestUpdateTime = updateOutputTimestampsWorker ( state , proj , /*anyDtsChange*/ false , Diagnostics . Updating_output_timestamps_of_project_0 ) ;
1723
1766
state . projectStatus . set ( resolvedPath , {
1724
1767
type : UpToDateStatusType . UpToDate ,
1725
- newestDeclarationFileContentChangedTime : priorNewestUpdateTime ,
1768
+ newestDeclarationFileContentChangedTime : priorNewestUpdateTime || getDtsChangeTime ( state , proj . options , resolvedPath ) ,
1726
1769
oldestOutputFileName : getFirstProjectOutput ( proj , ! state . host . useCaseSensitiveFileNames ( ) )
1727
1770
} ) ;
1728
1771
}
0 commit comments