@@ -75,6 +75,11 @@ export class ProjectManager implements Disposable {
75
75
*/
76
76
private ensuredModuleStructure ?: Observable < never > ;
77
77
78
+ /**
79
+ * Observable that completes when extra dependencies pointed to by tsconfig.json have been loaded.
80
+ */
81
+ private ensuredConfigDependencies ?: Observable < never > ;
82
+
78
83
/**
79
84
* Observable that completes when `ensureAllFiles` completed
80
85
*/
@@ -253,6 +258,7 @@ export class ProjectManager implements Disposable {
253
258
*/
254
259
invalidateModuleStructure ( ) : void {
255
260
this . ensuredModuleStructure = undefined ;
261
+ this . ensuredConfigDependencies = undefined ;
256
262
this . ensuredAllFiles = undefined ;
257
263
this . ensuredOwnFiles = undefined ;
258
264
}
@@ -322,6 +328,7 @@ export class ProjectManager implements Disposable {
322
328
span . addTags ( { uri, maxDepth } ) ;
323
329
ignore . add ( uri ) ;
324
330
return this . ensureModuleStructure ( span )
331
+ . concat ( Observable . defer ( ( ) => this . ensureConfigDependencies ( ) ) )
325
332
// If max depth was reached, don't go any further
326
333
. concat ( Observable . defer ( ( ) => maxDepth === 0 ? Observable . empty < never > ( ) : this . resolveReferencedFiles ( uri ) ) )
327
334
// Prevent cycles
@@ -338,6 +345,39 @@ export class ProjectManager implements Disposable {
338
345
} ) ;
339
346
}
340
347
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
+
341
381
/**
342
382
* Invalidates a cache entry for `resolveReferencedFiles` (e.g. because the file changed)
343
383
*
@@ -742,6 +782,11 @@ export class ProjectConfiguration {
742
782
*/
743
783
private expectedFilePaths = new Set < string > ( ) ;
744
784
785
+ /**
786
+ * List of resolved extra root directories to allow global type declaration files to be loaded from.
787
+ */
788
+ private typeRoots : string [ ] ;
789
+
745
790
/**
746
791
* @param fs file system to use
747
792
* @param documentRegistry Shared DocumentRegistry that manages SourceFile objects
@@ -846,6 +891,11 @@ export class ProjectConfiguration {
846
891
this . expectedFilePaths = new Set ( configParseResult . fileNames ) ;
847
892
848
893
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
+
849
899
if ( / ( ^ | \/ ) j s c o n f i g \. j s o n $ / . test ( this . configFilePath ) ) {
850
900
options . allowJs = true ;
851
901
}
@@ -872,6 +922,16 @@ export class ProjectConfiguration {
872
922
873
923
private ensuredBasicFiles = false ;
874
924
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
+
875
935
/**
876
936
* Ensures we added basic files (global TS files, dependencies, declarations)
877
937
*/
@@ -890,7 +950,8 @@ export class ProjectConfiguration {
890
950
// Add all global declaration files from the workspace and all declarations from the project
891
951
for ( const uri of this . fs . uris ( ) ) {
892
952
const fileName = uri2path ( uri ) ;
893
- if ( isGlobalTSFile ( fileName ) || ( isDeclarationFile ( fileName ) && this . expectedFilePaths . has ( toUnixPath ( fileName ) ) ) ) {
953
+ if ( isGlobalTSFile ( fileName ) ||
954
+ this . isExpectedDeclarationFile ( fileName ) ) {
894
955
const sourceFile = program . getSourceFile ( fileName ) ;
895
956
if ( ! sourceFile ) {
896
957
this . getHost ( ) . addFile ( fileName ) ;
0 commit comments