@@ -24,6 +24,8 @@ import {
24
24
uri2path
25
25
} from './util'
26
26
27
+ const LAST_FORWARD_OR_BACKWARD_SLASH = / [ \\ \/ ] [ ^ \\ \/ ] * $ /
28
+
27
29
/**
28
30
* Implementaton of LanguageServiceHost that works with in-memory file system.
29
31
* It takes file content from local cache and provides it to TS compiler on demand
@@ -183,6 +185,10 @@ export class InMemoryLanguageServiceHost implements ts.LanguageServiceHost {
183
185
* made available to the compiler before calling any other methods on
184
186
* the ProjectConfiguration or its public members. By default, no
185
187
* files are parsed.
188
+ *
189
+ * Windows file paths are converted to UNIX-style forward slashes
190
+ * when compared with Typescript configuration (isGlobalTSFile,
191
+ * expectedFilePaths and typeRoots)
186
192
*/
187
193
export class ProjectConfiguration {
188
194
@@ -225,12 +231,13 @@ export class ProjectConfiguration {
225
231
226
232
/**
227
233
* List of files that project consist of (based on tsconfig includes/excludes and wildcards).
228
- * Each item is a relative file path
234
+ * Each item is a relative UNIX-like file path
229
235
*/
230
236
private expectedFilePaths = new Set < string > ( )
231
237
232
238
/**
233
239
* List of resolved extra root directories to allow global type declaration files to be loaded from.
240
+ * Each item is an absolute UNIX-like file path
234
241
*/
235
242
private typeRoots : string [ ]
236
243
@@ -343,7 +350,7 @@ export class ProjectConfiguration {
343
350
const options = configParseResult . options
344
351
const pathResolver = / ^ [ a - z ] : \/ / i. test ( base ) ? path . win32 : path . posix
345
352
this . typeRoots = options . typeRoots ?
346
- options . typeRoots . map ( ( r : string ) => pathResolver . resolve ( this . rootFilePath , r ) ) :
353
+ options . typeRoots . map ( ( r : string ) => toUnixPath ( pathResolver . resolve ( this . rootFilePath , r ) ) ) :
347
354
[ ]
348
355
349
356
if ( / ( ^ | \/ ) j s c o n f i g \. j s o n $ / . test ( this . configFilePath ) ) {
@@ -401,11 +408,11 @@ export class ProjectConfiguration {
401
408
402
409
/**
403
410
* Determines if a fileName is a declaration file within expected files or type roots
404
- * @param fileName
411
+ * @param fileName A Unix-like absolute file path.
405
412
*/
406
413
public isExpectedDeclarationFile ( fileName : string ) : boolean {
407
414
return isDeclarationFile ( fileName ) &&
408
- ( this . expectedFilePaths . has ( toUnixPath ( fileName ) ) ||
415
+ ( this . expectedFilePaths . has ( fileName ) ||
409
416
this . typeRoots . some ( root => fileName . startsWith ( root ) ) )
410
417
}
411
418
@@ -427,8 +434,9 @@ export class ProjectConfiguration {
427
434
// Add all global declaration files from the workspace and all declarations from the project
428
435
for ( const uri of this . fs . uris ( ) ) {
429
436
const fileName = uri2path ( uri )
430
- if ( isGlobalTSFile ( fileName ) ||
431
- this . isExpectedDeclarationFile ( fileName ) ) {
437
+ const unixPath = toUnixPath ( fileName )
438
+ if ( isGlobalTSFile ( unixPath ) ||
439
+ this . isExpectedDeclarationFile ( unixPath ) ) {
432
440
const sourceFile = program . getSourceFile ( fileName )
433
441
if ( ! sourceFile ) {
434
442
this . getHost ( ) . addFile ( fileName )
@@ -487,6 +495,8 @@ export type ConfigType = 'js' | 'ts'
487
495
* makes one or more LanguageService objects. By default all LanguageService objects contain no files,
488
496
* they are added on demand - current file for hover or definition, project's files for references and
489
497
* all files from all projects for workspace symbols.
498
+ *
499
+ * ProjectManager preserves Windows paths until passed to ProjectConfiguration or TS APIs.
490
500
*/
491
501
export class ProjectManager implements Disposable {
492
502
@@ -588,7 +598,7 @@ export class ProjectManager implements Disposable {
588
598
589
599
// Create catch-all fallback configs in case there are no tsconfig.json files
590
600
// They are removed once at least one tsconfig.json is found
591
- const trimmedRootPath = this . rootPath . replace ( / \/ + $ / , '' )
601
+ const trimmedRootPath = this . rootPath . replace ( / [ \\ \/ ] + $ / , '' )
592
602
const fallbackConfigs : { js ?: ProjectConfiguration , ts ?: ProjectConfiguration } = { }
593
603
for ( const configType of [ 'js' , 'ts' ] as ConfigType [ ] ) {
594
604
const configs = this . configs [ configType ]
@@ -621,13 +631,8 @@ export class ProjectManager implements Disposable {
621
631
. filter ( ( [ uri , content ] ) => ! ! content && / \/ [ t j ] s c o n f i g \. j s o n / . test ( uri ) && ! uri . includes ( '/node_modules/' ) )
622
632
. subscribe ( ( [ uri , content ] ) => {
623
633
const filePath = uri2path ( uri )
624
- let dir = toUnixPath ( filePath )
625
- const pos = dir . lastIndexOf ( '/' )
626
- if ( pos <= 0 ) {
627
- dir = ''
628
- } else {
629
- dir = dir . substring ( 0 , pos )
630
- }
634
+ const pos = filePath . search ( LAST_FORWARD_OR_BACKWARD_SLASH )
635
+ const dir = pos <= 0 ? '' : filePath . substring ( 0 , pos )
631
636
const configType = this . getConfigurationType ( filePath )
632
637
const configs = this . configs [ configType ]
633
638
configs . set ( dir , new ProjectConfiguration (
@@ -813,7 +818,7 @@ export class ProjectManager implements Disposable {
813
818
814
819
/**
815
820
* Determines if a tsconfig/jsconfig needs additional declaration files loaded.
816
- * @param filePath
821
+ * @param filePath A UNIX-like absolute file path
817
822
*/
818
823
public isConfigDependency ( filePath : string ) : boolean {
819
824
for ( const config of this . configurations ( ) ) {
@@ -832,7 +837,7 @@ export class ProjectManager implements Disposable {
832
837
return traceObservable ( 'Ensure config dependencies' , childOf , span => {
833
838
if ( ! this . ensuredConfigDependencies ) {
834
839
this . ensuredConfigDependencies = observableFromIterable ( this . inMemoryFs . uris ( ) )
835
- . filter ( uri => this . isConfigDependency ( uri2path ( uri ) ) )
840
+ . filter ( uri => this . isConfigDependency ( toUnixPath ( uri2path ( uri ) ) ) )
836
841
. mergeMap ( uri => this . updater . ensure ( uri ) )
837
842
. do ( noop , err => {
838
843
this . ensuredConfigDependencies = undefined
@@ -929,19 +934,19 @@ export class ProjectManager implements Disposable {
929
934
* @return closest configuration for a given file path or undefined if there is no such configuration
930
935
*/
931
936
public getConfigurationIfExists ( filePath : string , configType = this . getConfigurationType ( filePath ) ) : ProjectConfiguration | undefined {
932
- let dir = toUnixPath ( filePath )
937
+ let dir = filePath
933
938
let config : ProjectConfiguration | undefined
934
939
const configs = this . configs [ configType ]
935
940
if ( ! configs ) {
936
941
return undefined
937
942
}
938
- const rootPath = this . rootPath . replace ( / \/ + $ / , '' )
943
+ const rootPath = this . rootPath . replace ( / [ \\ \/ ] + $ / , '' )
939
944
while ( dir && dir !== rootPath ) {
940
945
config = configs . get ( dir )
941
946
if ( config ) {
942
947
return config
943
948
}
944
- const pos = dir . lastIndexOf ( '/' )
949
+ const pos = dir . search ( LAST_FORWARD_OR_BACKWARD_SLASH )
945
950
if ( pos <= 0 ) {
946
951
dir = ''
947
952
} else {
@@ -1029,13 +1034,14 @@ export class ProjectManager implements Disposable {
1029
1034
* @return configuration type to use for a given file
1030
1035
*/
1031
1036
private getConfigurationType ( filePath : string ) : ConfigType {
1032
- const name = path . posix . basename ( filePath )
1037
+ const unixPath = toUnixPath ( filePath )
1038
+ const name = path . posix . basename ( unixPath )
1033
1039
if ( name === 'tsconfig.json' ) {
1034
1040
return 'ts'
1035
1041
} else if ( name === 'jsconfig.json' ) {
1036
1042
return 'js'
1037
1043
}
1038
- const extension = path . posix . extname ( filePath )
1044
+ const extension = path . posix . extname ( unixPath )
1039
1045
if ( extension === '.js' || extension === '.jsx' ) {
1040
1046
return 'js'
1041
1047
}
0 commit comments