@@ -156,6 +156,7 @@ import {
156156 HasChangedAutomaticTypeDirectiveNames ,
157157 hasChangesInResolutions ,
158158 hasExtension ,
159+ HasInvalidatedLibResolutions ,
159160 HasInvalidatedResolutions ,
160161 hasJSDocNodes ,
161162 hasJSFileExtension ,
@@ -211,6 +212,7 @@ import {
211212 JsxEmit ,
212213 length ,
213214 libMap ,
215+ LibResolution ,
214216 libs ,
215217 mapDefined ,
216218 mapDefinedIterator ,
@@ -276,6 +278,7 @@ import {
276278 ResolvedModuleWithFailedLookupLocations ,
277279 ResolvedProjectReference ,
278280 ResolvedTypeReferenceDirectiveWithFailedLookupLocations ,
281+ resolveLibrary ,
279282 resolveModuleName ,
280283 resolveTypeReferenceDirective ,
281284 returnFalse ,
@@ -1097,6 +1100,32 @@ function forEachProjectReference<T>(
10971100/** @internal */
10981101export const inferredTypesContainingFile = "__inferred type names__.ts" ;
10991102
1103+ /** @internal */
1104+ export function getInferredLibraryNameResolveFrom ( options : CompilerOptions , currentDirectory : string , libFileName : string ) {
1105+ const containingDirectory = options . configFilePath ? getDirectoryPath ( options . configFilePath ) : currentDirectory ;
1106+ return combinePaths ( containingDirectory , `__lib_node_modules_lookup_${ libFileName } __.ts` ) ;
1107+ }
1108+
1109+ function getLibraryNameFromLibFileName ( libFileName : string ) {
1110+ // Support resolving to lib.dom.d.ts -> @typescript/lib-dom, and
1111+ // lib.dom.iterable.d.ts -> @typescript/lib-dom/iterable
1112+ // lib.es2015.symbol.wellknown.d.ts -> @typescript/lib-es2015/symbol-wellknown
1113+ const components = libFileName . split ( "." ) ;
1114+ let path = components [ 1 ] ;
1115+ let i = 2 ;
1116+ while ( components [ i ] && components [ i ] !== "d" ) {
1117+ path += ( i === 2 ? "/" : "-" ) + components [ i ] ;
1118+ i ++ ;
1119+ }
1120+ return "@typescript/lib-" + path ;
1121+ }
1122+
1123+ function getLibFileNameFromLibReference ( libReference : FileReference ) {
1124+ const libName = toFileNameLowerCase ( libReference . fileName ) ;
1125+ const libFileName = libMap . get ( libName ) ;
1126+ return { libName, libFileName } ;
1127+ }
1128+
11001129interface DiagnosticCache < T extends Diagnostic > {
11011130 perFile ?: Map < Path , readonly T [ ] > ;
11021131 allDiagnostics ?: readonly T [ ] ;
@@ -1176,6 +1205,7 @@ export function isProgramUptoDate(
11761205 getSourceVersion : ( path : Path , fileName : string ) => string | undefined ,
11771206 fileExists : ( fileName : string ) => boolean ,
11781207 hasInvalidatedResolutions : HasInvalidatedResolutions ,
1208+ hasInvalidatedLibResolutions : HasInvalidatedLibResolutions ,
11791209 hasChangedAutomaticTypeDirectiveNames : HasChangedAutomaticTypeDirectiveNames | undefined ,
11801210 getParsedCommandLine : ( fileName : string ) => ParsedCommandLine | undefined ,
11811211 projectReferences : readonly ProjectReference [ ] | undefined
@@ -1201,6 +1231,9 @@ export function isProgramUptoDate(
12011231 // If the compilation settings do no match, then the program is not up-to-date
12021232 if ( ! compareDataObjects ( currentOptions , newOptions ) ) return false ;
12031233
1234+ // If library resolution is invalidated, then the program is not up-to-date
1235+ if ( program . resolvedLibReferences && forEachEntry ( program . resolvedLibReferences , ( _value , libFileName ) => hasInvalidatedLibResolutions ( libFileName ) ) ) return false ;
1236+
12041237 // If everything matches but the text of config file is changed,
12051238 // error locations can change for program options, so update the program
12061239 if ( currentOptions . configFile && newOptions . configFile ) return currentOptions . configFile . text === newOptions . configFile . text ;
@@ -1469,6 +1502,9 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
14691502 let automaticTypeDirectiveNames : string [ ] | undefined ;
14701503 let automaticTypeDirectiveResolutions : ModeAwareCache < ResolvedTypeReferenceDirectiveWithFailedLookupLocations > ;
14711504
1505+ let resolvedLibReferences : Map < string , LibResolution > | undefined ;
1506+ let resolvedLibProcessing : Map < string , LibResolution > | undefined ;
1507+
14721508 // The below settings are to track if a .js file should be add to the program if loaded via searching under node_modules.
14731509 // This works as imported modules are discovered recursively in a depth first manner, specifically:
14741510 // - For each root file, findSourceFile is called.
@@ -1592,6 +1628,17 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
15921628 ) ;
15931629 }
15941630
1631+ const hasInvalidatedLibResolutions = host . hasInvalidatedLibResolutions || returnFalse ;
1632+ let actualResolveLibrary : ( libraryName : string , resolveFrom : string , options : CompilerOptions , libFileName : string ) => ResolvedModuleWithFailedLookupLocations ;
1633+ if ( host . resolveLibrary ) {
1634+ actualResolveLibrary = host . resolveLibrary . bind ( host ) ;
1635+ }
1636+ else {
1637+ const libraryResolutionCache = createModuleResolutionCache ( currentDirectory , getCanonicalFileName , options , moduleResolutionCache ?. getPackageJsonInfoCache ( ) ) ;
1638+ actualResolveLibrary = ( libraryName , resolveFrom , options ) =>
1639+ resolveLibrary ( libraryName , resolveFrom , options , host , libraryResolutionCache ) ;
1640+ }
1641+
15951642 // Map from a stringified PackageId to the source file with that id.
15961643 // Only one source file may have a given packageId. Others become redirects (see createRedirectSourceFile).
15971644 // `packageIdToSourceFile` is only used while building the program, while `sourceFileToPackageName` and `isSourceFileTargetOfRedirect` are kept around.
@@ -1772,6 +1819,7 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
17721819
17731820 // unconditionally set oldProgram to undefined to prevent it from being captured in closure
17741821 oldProgram = undefined ;
1822+ resolvedLibProcessing = undefined ;
17751823
17761824 const program : Program = {
17771825 getRootFileNames : ( ) => rootNames ,
@@ -1813,6 +1861,7 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
18131861 sourceFileToPackageName,
18141862 redirectTargetsMap,
18151863 usesUriStyleNodeCoreModules,
1864+ resolvedLibReferences,
18161865 isEmittedFile,
18171866 getConfigFileParsingDiagnostics,
18181867 getProjectReferences,
@@ -2441,6 +2490,11 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
24412490 return StructureIsReused . SafeModules ;
24422491 }
24432492
2493+ if ( oldProgram . resolvedLibReferences &&
2494+ forEachEntry ( oldProgram . resolvedLibReferences , ( resolution , libFileName ) => pathForLibFileWorker ( libFileName ) . actual !== resolution . actual ) ) {
2495+ return StructureIsReused . SafeModules ;
2496+ }
2497+
24442498 if ( host . hasChangedAutomaticTypeDirectiveNames ) {
24452499 if ( host . hasChangedAutomaticTypeDirectiveNames ( ) ) return StructureIsReused . SafeModules ;
24462500 }
@@ -2481,6 +2535,7 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
24812535 sourceFileToPackageName = oldProgram . sourceFileToPackageName ;
24822536 redirectTargetsMap = oldProgram . redirectTargetsMap ;
24832537 usesUriStyleNodeCoreModules = oldProgram . usesUriStyleNodeCoreModules ;
2538+ resolvedLibReferences = oldProgram . resolvedLibReferences ;
24842539
24852540 return StructureIsReused . Completely ;
24862541 }
@@ -2596,7 +2651,7 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
25962651 return equalityComparer ( file . fileName , getDefaultLibraryFileName ( ) ) ;
25972652 }
25982653 else {
2599- return some ( options . lib , libFileName => equalityComparer ( file . fileName , pathForLibFile ( libFileName ) ) ) ;
2654+ return some ( options . lib , libFileName => equalityComparer ( file . fileName , resolvedLibReferences ! . get ( libFileName ) ! . actual ) ) ;
26002655 }
26012656 }
26022657
@@ -3310,11 +3365,9 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
33103365 }
33113366
33123367 function getLibFileFromReference ( ref : FileReference ) {
3313- const libName = toFileNameLowerCase ( ref . fileName ) ;
3314- const libFileName = libMap . get ( libName ) ;
3315- if ( libFileName ) {
3316- return getSourceFile ( pathForLibFile ( libFileName ) ) ;
3317- }
3368+ const { libFileName } = getLibFileNameFromLibReference ( ref ) ;
3369+ const actualFileName = libFileName && resolvedLibReferences ?. get ( libFileName ) ?. actual ;
3370+ return actualFileName !== undefined ? getSourceFile ( actualFileName ) : undefined ;
33183371 }
33193372
33203373 /** This should have similar behavior to 'processSourceFile' without diagnostics or mutation. */
@@ -3810,29 +3863,61 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
38103863 }
38113864
38123865 function pathForLibFile ( libFileName : string ) : string {
3813- // Support resolving to lib.dom.d.ts -> @typescript/lib-dom, and
3814- // lib.dom.iterable.d.ts -> @typescript/lib-dom/iterable
3815- // lib.es2015.symbol.wellknown.d.ts -> @typescript/lib-es2015/symbol-wellknown
3816- const components = libFileName . split ( "." ) ;
3817- let path = components [ 1 ] ;
3818- let i = 2 ;
3819- while ( components [ i ] && components [ i ] !== "d" ) {
3820- path += ( i === 2 ? "/" : "-" ) + components [ i ] ;
3821- i ++ ;
3822- }
3823- const containingDirectory = options . configFilePath ? getDirectoryPath ( options . configFilePath ) : currentDirectory ;
3824- const resolveFrom = combinePaths ( containingDirectory , `__lib_node_modules_lookup_${ libFileName } __.ts` ) ;
3825- const localOverrideModuleResult = resolveModuleName ( "@typescript/lib-" + path , resolveFrom , { moduleResolution : ModuleResolutionKind . Node10 , traceResolution : options . traceResolution } , host , moduleResolutionCache ) ;
3826- if ( localOverrideModuleResult ?. resolvedModule ) {
3827- return localOverrideModuleResult . resolvedModule . resolvedFileName ;
3866+ const existing = resolvedLibReferences ?. get ( libFileName ) ;
3867+ if ( existing ) return existing . actual ;
3868+ const result = pathForLibFileWorker ( libFileName ) ;
3869+ ( resolvedLibReferences ??= new Map ( ) ) . set ( libFileName , result ) ;
3870+ return result . actual ;
3871+ }
3872+
3873+ function pathForLibFileWorker ( libFileName : string ) : LibResolution {
3874+ const existing = resolvedLibProcessing ?. get ( libFileName ) ;
3875+ if ( existing ) return existing ;
3876+
3877+ if ( structureIsReused !== StructureIsReused . Not && oldProgram && ! hasInvalidatedLibResolutions ( libFileName ) ) {
3878+ const oldResolution = oldProgram . resolvedLibReferences ?. get ( libFileName ) ;
3879+ if ( oldResolution ) {
3880+ if ( oldResolution . resolution && isTraceEnabled ( options , host ) ) {
3881+ const libraryName = getLibraryNameFromLibFileName ( libFileName ) ;
3882+ const resolveFrom = getInferredLibraryNameResolveFrom ( options , currentDirectory , libFileName ) ;
3883+ trace ( host ,
3884+ oldResolution . resolution . resolvedModule ?
3885+ oldResolution . resolution . resolvedModule . packageId ?
3886+ Diagnostics . Reusing_resolution_of_module_0_from_1_of_old_program_it_was_successfully_resolved_to_2_with_Package_ID_3 :
3887+ Diagnostics . Reusing_resolution_of_module_0_from_1_of_old_program_it_was_successfully_resolved_to_2 :
3888+ Diagnostics . Reusing_resolution_of_module_0_from_1_of_old_program_it_was_not_resolved ,
3889+ libraryName ,
3890+ getNormalizedAbsolutePath ( resolveFrom , currentDirectory ) ,
3891+ oldResolution . resolution . resolvedModule ?. resolvedFileName ,
3892+ oldResolution . resolution . resolvedModule ?. packageId && packageIdToString ( oldResolution . resolution . resolvedModule . packageId )
3893+ ) ;
3894+ }
3895+ ( resolvedLibProcessing ??= new Map ( ) ) . set ( libFileName , oldResolution ) ;
3896+ return oldResolution ;
3897+ }
38283898 }
3829- return combinePaths ( defaultLibraryPath , libFileName ) ;
3899+
3900+ const libraryName = getLibraryNameFromLibFileName ( libFileName ) ;
3901+ const resolveFrom = getInferredLibraryNameResolveFrom ( options , currentDirectory , libFileName ) ;
3902+ tracing ?. push ( tracing . Phase . Program , "resolveLibrary" , { resolveFrom } ) ;
3903+ performance . mark ( "beforeResolveLibrary" ) ;
3904+ const resolution = actualResolveLibrary ( libraryName , resolveFrom , options , libFileName ) ;
3905+ performance . mark ( "afterResolveLibrary" ) ;
3906+ performance . measure ( "ResolveLibrary" , "beforeResolveLibrary" , "afterResolveLibrary" ) ;
3907+ tracing ?. pop ( ) ;
3908+ const result : LibResolution = {
3909+ resolution,
3910+ actual : resolution . resolvedModule ?
3911+ resolution . resolvedModule . resolvedFileName :
3912+ combinePaths ( defaultLibraryPath , libFileName )
3913+ } ;
3914+ ( resolvedLibProcessing ??= new Map ( ) ) . set ( libFileName , result ) ;
3915+ return result ;
38303916 }
38313917
38323918 function processLibReferenceDirectives ( file : SourceFile ) {
38333919 forEach ( file . libReferenceDirectives , ( libReference , index ) => {
3834- const libName = toFileNameLowerCase ( libReference . fileName ) ;
3835- const libFileName = libMap . get ( libName ) ;
3920+ const { libName, libFileName } = getLibFileNameFromLibReference ( libReference ) ;
38363921 if ( libFileName ) {
38373922 // we ignore any 'no-default-lib' reference set on this file.
38383923 processRootFile ( pathForLibFile ( libFileName ) , /*isDefaultLib*/ true , /*ignoreNoDefaultLib*/ true , { kind : FileIncludeKind . LibReferenceDirective , file : file . path , index, } ) ;
0 commit comments