@@ -75,6 +75,11 @@ export class ProjectManager implements Disposable {
7575 */
7676 private ensuredModuleStructure ?: Observable < never > ;
7777
78+ /**
79+ * Observable that completes when extra dependencies pointed to by tsconfig.json have been loaded.
80+ */
81+ private ensuredConfigDependencies ?: Observable < never > ;
82+
7883 /**
7984 * Observable that completes when `ensureAllFiles` completed
8085 */
@@ -253,6 +258,7 @@ export class ProjectManager implements Disposable {
253258 */
254259 invalidateModuleStructure ( ) : void {
255260 this . ensuredModuleStructure = undefined ;
261+ this . ensuredConfigDependencies = undefined ;
256262 this . ensuredAllFiles = undefined ;
257263 this . ensuredOwnFiles = undefined ;
258264 }
@@ -322,6 +328,7 @@ export class ProjectManager implements Disposable {
322328 span . addTags ( { uri, maxDepth } ) ;
323329 ignore . add ( uri ) ;
324330 return this . ensureModuleStructure ( span )
331+ . concat ( Observable . defer ( ( ) => this . ensureConfigDependencies ( ) ) )
325332 // If max depth was reached, don't go any further
326333 . concat ( Observable . defer ( ( ) => maxDepth === 0 ? Observable . empty < never > ( ) : this . resolveReferencedFiles ( uri ) ) )
327334 // Prevent cycles
@@ -338,6 +345,39 @@ export class ProjectManager implements Disposable {
338345 } ) ;
339346 }
340347
348+ /**
349+ * Determines if a tsconfig/jsconfig needs additional declaration files loaded.
350+ * @param filePath
351+ */
352+ isConfigDependency ( filePath : string ) : boolean {
353+ for ( const config of this . configurations ( ) ) {
354+ config . ensureConfigFile ( ) ;
355+ if ( config . isExpectedDeclarationFile ( filePath ) ) {
356+ return true ;
357+ }
358+ }
359+ return false ;
360+ }
361+
362+ /**
363+ * Loads files determined by tsconfig to be needed into the file system
364+ */
365+ ensureConfigDependencies ( childOf = new Span ( ) ) : Observable < never > {
366+ return traceObservable ( 'Ensure config dependencies' , childOf , span => {
367+ if ( ! this . ensuredConfigDependencies ) {
368+ this . ensuredConfigDependencies = observableFromIterable ( this . inMemoryFs . uris ( ) )
369+ . filter ( uri => this . isConfigDependency ( uri2path ( uri ) ) )
370+ . mergeMap ( uri => this . updater . ensure ( uri ) )
371+ . do ( noop , err => {
372+ this . ensuredConfigDependencies = undefined ;
373+ } )
374+ . publishReplay ( )
375+ . refCount ( ) as Observable < never > ;
376+ }
377+ return this . ensuredConfigDependencies ;
378+ } ) ;
379+ }
380+
341381 /**
342382 * Invalidates a cache entry for `resolveReferencedFiles` (e.g. because the file changed)
343383 *
@@ -742,6 +782,11 @@ export class ProjectConfiguration {
742782 */
743783 private expectedFilePaths = new Set < string > ( ) ;
744784
785+ /**
786+ * List of resolved extra root directories to allow global type declaration files to be loaded from.
787+ */
788+ private typeRoots : string [ ] ;
789+
745790 /**
746791 * @param fs file system to use
747792 * @param documentRegistry Shared DocumentRegistry that manages SourceFile objects
@@ -846,6 +891,11 @@ export class ProjectConfiguration {
846891 this . expectedFilePaths = new Set ( configParseResult . fileNames ) ;
847892
848893 const options = configParseResult . options ;
894+ const pathResolver = / ^ [ a - z ] : \/ / i. test ( base ) ? path . win32 : path . posix ;
895+ this . typeRoots = options . typeRoots ?
896+ options . typeRoots . map ( ( r : string ) => pathResolver . resolve ( this . rootFilePath , r ) ) :
897+ [ ] ;
898+
849899 if ( / ( ^ | \/ ) j s c o n f i g \. j s o n $ / . test ( this . configFilePath ) ) {
850900 options . allowJs = true ;
851901 }
@@ -872,6 +922,16 @@ export class ProjectConfiguration {
872922
873923 private ensuredBasicFiles = false ;
874924
925+ /**
926+ * Determines if a fileName is a declaration file within expected files or type roots
927+ * @param fileName
928+ */
929+ public isExpectedDeclarationFile ( fileName : string ) {
930+ return isDeclarationFile ( fileName ) &&
931+ ( this . expectedFilePaths . has ( toUnixPath ( fileName ) ) ||
932+ this . typeRoots . some ( root => fileName . startsWith ( root ) ) ) ;
933+ }
934+
875935 /**
876936 * Ensures we added basic files (global TS files, dependencies, declarations)
877937 */
@@ -890,7 +950,8 @@ export class ProjectConfiguration {
890950 // Add all global declaration files from the workspace and all declarations from the project
891951 for ( const uri of this . fs . uris ( ) ) {
892952 const fileName = uri2path ( uri ) ;
893- if ( isGlobalTSFile ( fileName ) || ( isDeclarationFile ( fileName ) && this . expectedFilePaths . has ( toUnixPath ( fileName ) ) ) ) {
953+ if ( isGlobalTSFile ( fileName ) ||
954+ this . isExpectedDeclarationFile ( fileName ) ) {
894955 const sourceFile = program . getSourceFile ( fileName ) ;
895956 if ( ! sourceFile ) {
896957 this . getHost ( ) . addFile ( fileName ) ;
0 commit comments