@@ -454,6 +454,8 @@ namespace ts {
454
454
return false ;
455
455
}
456
456
457
+ let seenResolvedRefs : ResolvedProjectReference [ ] | undefined ;
458
+
457
459
// If project references dont match
458
460
if ( ! arrayIsEqualTo ( program . getProjectReferences ( ) , projectReferences , projectReferenceUptoDate ) ) {
459
461
return false ;
@@ -496,11 +498,29 @@ namespace ts {
496
498
if ( ! projectReferenceIsEqualTo ( oldRef , newRef ) ) {
497
499
return false ;
498
500
}
499
- const oldResolvedRef = program ! . getResolvedProjectReferences ( ) ! [ index ] ;
501
+ return resolvedProjectReferenceUptoDate ( program ! . getResolvedProjectReferences ( ) ! [ index ] , oldRef ) ;
502
+ }
503
+
504
+ function resolvedProjectReferenceUptoDate ( oldResolvedRef : ResolvedProjectReference | undefined , oldRef : ProjectReference ) : boolean {
500
505
if ( oldResolvedRef ) {
506
+ if ( contains ( seenResolvedRefs , oldResolvedRef ) ) {
507
+ // Assume true
508
+ return true ;
509
+ }
510
+
501
511
// If sourceFile for the oldResolvedRef existed, check the version for uptodate
502
- return sourceFileVersionUptoDate ( oldResolvedRef . sourceFile ) ;
512
+ if ( ! sourceFileVersionUptoDate ( oldResolvedRef . sourceFile ) ) {
513
+ return false ;
514
+ }
515
+
516
+ // Add to seen before checking the referenced paths of this config file
517
+ ( seenResolvedRefs || ( seenResolvedRefs = [ ] ) ) . push ( oldResolvedRef ) ;
518
+
519
+ // If child project references are upto date, this project reference is uptodate
520
+ return ! forEach ( oldResolvedRef . references , ( childResolvedRef , index ) =>
521
+ ! resolvedProjectReferenceUptoDate ( childResolvedRef , oldResolvedRef . commandLine . projectReferences ! [ index ] ) ) ;
503
522
}
523
+
504
524
// In old program, not able to resolve project reference path,
505
525
// so if config file doesnt exist, it is uptodate.
506
526
return ! fileExists ( resolveProjectReferencePath ( oldRef ) ) ;
@@ -662,8 +682,8 @@ namespace ts {
662
682
const filesByNameIgnoreCase = host . useCaseSensitiveFileNames ( ) ? createMap < SourceFile > ( ) : undefined ;
663
683
664
684
// A parallel array to projectReferences storing the results of reading in the referenced tsconfig files
665
- let resolvedProjectReferences : ( ResolvedProjectReference | undefined ) [ ] | undefined = projectReferences ? [ ] : undefined ;
666
- let projectReferenceRedirects : ParsedCommandLine [ ] | undefined ;
685
+ let resolvedProjectReferences : ReadonlyArray < ResolvedProjectReference | undefined > | undefined ;
686
+ let projectReferenceRedirects : Map < ResolvedProjectReference | false > | undefined ;
667
687
668
688
const shouldCreateNewSourceFile = shouldProgramCreateNewSourceFiles ( oldProgram , options ) ;
669
689
const structuralIsReused = tryReuseStructureFromOldProgram ( ) ;
@@ -672,16 +692,16 @@ namespace ts {
672
692
processingOtherFiles = [ ] ;
673
693
674
694
if ( projectReferences ) {
675
- for ( const ref of projectReferences ) {
676
- const parsedRef = parseProjectReferenceConfigFile ( ref ) ;
677
- resolvedProjectReferences ! . push ( parsedRef ) ;
695
+ if ( ! resolvedProjectReferences ) {
696
+ resolvedProjectReferences = projectReferences . map ( parseProjectReferenceConfigFile ) ;
697
+ }
698
+ for ( const parsedRef of resolvedProjectReferences ) {
678
699
if ( parsedRef ) {
679
700
const out = parsedRef . commandLine . options . outFile || parsedRef . commandLine . options . out ;
680
701
if ( out ) {
681
702
const dtsOutfile = changeExtension ( out , ".d.ts" ) ;
682
703
processSourceFile ( dtsOutfile , /*isDefaultLib*/ false , /*ignoreNoDefaultLib*/ false , /*packageId*/ undefined ) ;
683
704
}
684
- addProjectReferenceRedirects ( parsedRef . commandLine ) ;
685
705
}
686
706
}
687
707
}
@@ -740,6 +760,12 @@ namespace ts {
740
760
741
761
// unconditionally set oldProgram to undefined to prevent it from being captured in closure
742
762
oldProgram = undefined ;
763
+ // Do not use our own command line for projectReferenceRedirects
764
+ if ( projectReferenceRedirects ) {
765
+ Debug . assert ( ! ! options . configFilePath ) ;
766
+ const path = toPath ( options . configFilePath ! ) ;
767
+ projectReferenceRedirects . delete ( path ) ;
768
+ }
743
769
744
770
program = {
745
771
getRootFileNames : ( ) => rootNames ,
@@ -1001,6 +1027,48 @@ namespace ts {
1001
1027
}
1002
1028
}
1003
1029
1030
+ function canReuseProjectReferences (
1031
+ newProjectReferences : ReadonlyArray < ProjectReference > | undefined ,
1032
+ oldProjectReferences : ReadonlyArray < ProjectReference > | undefined ,
1033
+ oldResolvedReferences : ReadonlyArray < ResolvedProjectReference | undefined > | undefined ) : boolean {
1034
+ // If array of references is changed, we cant resue old program
1035
+ if ( ! arrayIsEqualTo ( oldProjectReferences ! , newProjectReferences , projectReferenceIsEqualTo ) ) {
1036
+ return false ;
1037
+ }
1038
+
1039
+ // Check the json files for the project references
1040
+ if ( newProjectReferences ) {
1041
+ // Resolved project referenced should be array if projectReferences provided are array
1042
+ Debug . assert ( ! ! oldResolvedReferences ) ;
1043
+ for ( let i = 0 ; i < newProjectReferences . length ; i ++ ) {
1044
+ const oldRef = oldResolvedReferences ! [ i ] ;
1045
+ const newRef = parseProjectReferenceConfigFile ( newProjectReferences [ i ] ) ;
1046
+ if ( oldRef ) {
1047
+ if ( ! newRef || newRef . sourceFile !== oldRef . sourceFile ) {
1048
+ // Resolved project reference has gone missing or changed
1049
+ return false ;
1050
+ }
1051
+
1052
+ // If the transitive references can be reused then only this reference can be reused
1053
+ if ( ! canReuseProjectReferences ( newRef . commandLine . projectReferences , oldRef . commandLine . projectReferences , oldRef . references ) ) {
1054
+ return false ;
1055
+ }
1056
+ }
1057
+ else {
1058
+ // A previously-unresolved reference may be resolved now
1059
+ if ( newRef !== undefined ) {
1060
+ return false ;
1061
+ }
1062
+ }
1063
+ }
1064
+ }
1065
+ else {
1066
+ // Resolved project referenced should be undefined if projectReferences is undefined
1067
+ Debug . assert ( ! oldResolvedReferences ) ;
1068
+ }
1069
+ return true ;
1070
+ }
1071
+
1004
1072
function tryReuseStructureFromOldProgram ( ) : StructureIsReused {
1005
1073
if ( ! oldProgram ) {
1006
1074
return StructureIsReused . Not ;
@@ -1026,39 +1094,10 @@ namespace ts {
1026
1094
}
1027
1095
1028
1096
// Check if any referenced project tsconfig files are different
1029
-
1030
- // If array of references is changed, we cant resue old program
1031
- const oldProjectReferences = oldProgram . getProjectReferences ( ) ;
1032
- if ( ! arrayIsEqualTo ( oldProjectReferences ! , projectReferences , projectReferenceIsEqualTo ) ) {
1097
+ if ( ! canReuseProjectReferences ( projectReferences , oldProgram . getProjectReferences ( ) , oldProgram . getResolvedProjectReferences ( ) ) ) {
1033
1098
return oldProgram . structureIsReused = StructureIsReused . Not ;
1034
1099
}
1035
-
1036
- // Check the json files for the project references
1037
- const oldRefs = oldProgram . getResolvedProjectReferences ( ) ;
1038
- if ( projectReferences ) {
1039
- // Resolved project referenced should be array if projectReferences provided are array
1040
- Debug . assert ( ! ! oldRefs ) ;
1041
- for ( let i = 0 ; i < projectReferences . length ; i ++ ) {
1042
- const oldRef = oldRefs ! [ i ] ;
1043
- const newRef = parseProjectReferenceConfigFile ( projectReferences [ i ] ) ;
1044
- if ( oldRef ) {
1045
- if ( ! newRef || newRef . sourceFile !== oldRef . sourceFile ) {
1046
- // Resolved project reference has gone missing or changed
1047
- return oldProgram . structureIsReused = StructureIsReused . Not ;
1048
- }
1049
- }
1050
- else {
1051
- // A previously-unresolved reference may be resolved now
1052
- if ( newRef !== undefined ) {
1053
- return oldProgram . structureIsReused = StructureIsReused . Not ;
1054
- }
1055
- }
1056
- }
1057
- }
1058
- else {
1059
- // Resolved project referenced should be undefined if projectReferences is undefined
1060
- Debug . assert ( ! oldRefs ) ;
1061
- }
1100
+ resolvedProjectReferences = oldProgram . getResolvedProjectReferences ( ) ;
1062
1101
1063
1102
// check if program source files has changed in the way that can affect structure of the program
1064
1103
const newSourceFiles : SourceFile [ ] = [ ] ;
@@ -1248,14 +1287,6 @@ namespace ts {
1248
1287
fileProcessingDiagnostics . reattachFileDiagnostics ( modifiedFile . newFile ) ;
1249
1288
}
1250
1289
resolvedTypeReferenceDirectives = oldProgram . getResolvedTypeReferenceDirectives ( ) ;
1251
- resolvedProjectReferences = oldProgram . getResolvedProjectReferences ( ) ;
1252
- if ( resolvedProjectReferences ) {
1253
- resolvedProjectReferences . forEach ( ref => {
1254
- if ( ref ) {
1255
- addProjectReferenceRedirects ( ref . commandLine ) ;
1256
- }
1257
- } ) ;
1258
- }
1259
1290
1260
1291
sourceFileToPackageName = oldProgram . sourceFileToPackageName ;
1261
1292
redirectTargetsMap = oldProgram . redirectTargetsMap ;
@@ -2182,22 +2213,22 @@ namespace ts {
2182
2213
2183
2214
function getProjectReferenceRedirect ( fileName : string ) : string | undefined {
2184
2215
// Ignore dts or any of the non ts files
2185
- if ( ! projectReferenceRedirects || fileExtensionIs ( fileName , Extension . Dts ) || ! fileExtensionIsOneOf ( fileName , supportedTSExtensions ) ) {
2216
+ if ( ! resolvedProjectReferences || ! resolvedProjectReferences . length || fileExtensionIs ( fileName , Extension . Dts ) || ! fileExtensionIsOneOf ( fileName , supportedTSExtensions ) ) {
2186
2217
return undefined ;
2187
2218
}
2188
2219
2189
2220
// If this file is produced by a referenced project, we need to rewrite it to
2190
2221
// look in the output folder of the referenced project rather than the input
2191
- return forEach ( projectReferenceRedirects , referencedProject => {
2222
+ return forEachEntry ( projectReferenceRedirects ! , referencedProject => {
2192
2223
// not input file from the referenced project, ignore
2193
- if ( ! contains ( referencedProject . fileNames , fileName , isSameFile ) ) {
2224
+ if ( ! referencedProject || ! contains ( referencedProject . commandLine . fileNames , fileName , isSameFile ) ) {
2194
2225
return undefined ;
2195
2226
}
2196
2227
2197
- const out = referencedProject . options . outFile || referencedProject . options . out ;
2228
+ const out = referencedProject . commandLine . options . outFile || referencedProject . commandLine . options . out ;
2198
2229
return out ?
2199
2230
changeExtension ( out , Extension . Dts ) :
2200
- getOutputDeclarationFileName ( fileName , referencedProject ) ;
2231
+ getOutputDeclarationFileName ( fileName , referencedProject . commandLine ) ;
2201
2232
} ) ;
2202
2233
}
2203
2234
@@ -2388,22 +2419,34 @@ namespace ts {
2388
2419
return allFilesBelongToPath ;
2389
2420
}
2390
2421
2391
- function parseProjectReferenceConfigFile ( ref : ProjectReference ) : { commandLine : ParsedCommandLine , sourceFile : SourceFile } | undefined {
2422
+ function parseProjectReferenceConfigFile ( ref : ProjectReference ) : ResolvedProjectReference | undefined {
2423
+ if ( ! projectReferenceRedirects ) {
2424
+ projectReferenceRedirects = createMap < ResolvedProjectReference | false > ( ) ;
2425
+ }
2426
+
2392
2427
// The actual filename (i.e. add "/tsconfig.json" if necessary)
2393
2428
const refPath = resolveProjectReferencePath ( ref ) ;
2429
+ const sourceFilePath = toPath ( refPath ) ;
2430
+ const fromCache = projectReferenceRedirects . get ( sourceFilePath ) ;
2431
+ if ( fromCache !== undefined ) {
2432
+ return fromCache || undefined ;
2433
+ }
2434
+
2394
2435
// An absolute path pointing to the containing directory of the config file
2395
2436
const basePath = getNormalizedAbsolutePath ( getDirectoryPath ( refPath ) , host . getCurrentDirectory ( ) ) ;
2396
2437
const sourceFile = host . getSourceFile ( refPath , ScriptTarget . JSON ) as JsonSourceFile | undefined ;
2397
2438
if ( sourceFile === undefined ) {
2439
+ projectReferenceRedirects . set ( sourceFilePath , false ) ;
2398
2440
return undefined ;
2399
2441
}
2400
- sourceFile . path = toPath ( refPath ) ;
2442
+ sourceFile . path = sourceFilePath ;
2401
2443
const commandLine = parseJsonSourceFileConfigFileContent ( sourceFile , configParsingHost , basePath , /*existingOptions*/ undefined , refPath ) ;
2402
- return { commandLine, sourceFile } ;
2403
- }
2404
-
2405
- function addProjectReferenceRedirects ( referencedProject : ParsedCommandLine ) {
2406
- ( projectReferenceRedirects || ( projectReferenceRedirects = [ ] ) ) . push ( referencedProject ) ;
2444
+ const resolvedRef : ResolvedProjectReference = { commandLine, sourceFile } ;
2445
+ projectReferenceRedirects . set ( sourceFilePath , resolvedRef ) ;
2446
+ if ( commandLine . projectReferences ) {
2447
+ resolvedRef . references = commandLine . projectReferences . map ( parseProjectReferenceConfigFile ) ;
2448
+ }
2449
+ return resolvedRef ;
2407
2450
}
2408
2451
2409
2452
function verifyCompilerOptions ( ) {
0 commit comments