1
+ import {
2
+ getModuleSpecifierPreferences ,
3
+ } from "../compiler/moduleSpecifiers" ;
1
4
import {
2
5
addToSeen ,
3
6
altDirectorySeparator ,
@@ -52,7 +55,6 @@ import {
52
55
getEffectiveTypeRoots ,
53
56
getEmitModuleResolutionKind ,
54
57
getLeadingCommentRanges ,
55
- getModuleSpecifierEndingPreference ,
56
58
getOwnKeys ,
57
59
getPackageJsonTypesVersionsPaths ,
58
60
getPathComponents ,
@@ -744,7 +746,7 @@ function getCompletionEntriesForDirectoryFragment(
744
746
continue ;
745
747
}
746
748
747
- const { name, extension } = getFilenameWithExtensionOption ( getBaseFileName ( filePath ) , host . getCompilationSettings ( ) , extensionOptions ) ;
749
+ const { name, extension } = getFilenameWithExtensionOption ( getBaseFileName ( filePath ) , host . getCompilationSettings ( ) , extensionOptions , /*isExportsWildcard*/ false ) ;
748
750
result . add ( nameAndKind ( name , ScriptElementKind . scriptElement , extension ) ) ;
749
751
}
750
752
}
@@ -764,7 +766,7 @@ function getCompletionEntriesForDirectoryFragment(
764
766
return result ;
765
767
}
766
768
767
- function getFilenameWithExtensionOption ( name : string , compilerOptions : CompilerOptions , extensionOptions : ExtensionOptions ) : { name : string ; extension : Extension | undefined ; } {
769
+ function getFilenameWithExtensionOption ( name : string , compilerOptions : CompilerOptions , extensionOptions : ExtensionOptions , isExportsWildcard : boolean ) : { name : string ; extension : Extension | undefined ; } {
768
770
const nonJsResult = moduleSpecifiers . tryGetRealFileNameForNonJsDeclarationFileName ( name ) ;
769
771
if ( nonJsResult ) {
770
772
return { name : nonJsResult , extension : tryGetExtensionFromPath ( nonJsResult ) } ;
@@ -773,8 +775,19 @@ function getFilenameWithExtensionOption(name: string, compilerOptions: CompilerO
773
775
return { name, extension : tryGetExtensionFromPath ( name ) } ;
774
776
}
775
777
776
- const endingPreference = getModuleSpecifierEndingPreference ( extensionOptions . endingPreference , extensionOptions . resolutionMode , compilerOptions , extensionOptions . importingSourceFile ) ;
777
- if ( endingPreference === ModuleSpecifierEnding . TsExtension ) {
778
+ let allowedEndings = getModuleSpecifierPreferences (
779
+ { importModuleSpecifierEnding : extensionOptions . endingPreference } ,
780
+ compilerOptions ,
781
+ extensionOptions . importingSourceFile ,
782
+ ) . getAllowedEndingsInPreferredOrder ( extensionOptions . resolutionMode ) ;
783
+
784
+ if ( isExportsWildcard ) {
785
+ // If we're completing `import {} from "foo/|"` and subpaths are available via `"exports": { "./*": "./src/*" }`,
786
+ // the completion must be a (potentially extension-swapped) file name. Dropping extensions and index files is not allowed.
787
+ allowedEndings = allowedEndings . filter ( e => e !== ModuleSpecifierEnding . Minimal && e !== ModuleSpecifierEnding . Index ) ;
788
+ }
789
+
790
+ if ( allowedEndings [ 0 ] === ModuleSpecifierEnding . TsExtension ) {
778
791
if ( fileExtensionIsOneOf ( name , supportedTSImplementationExtensions ) ) {
779
792
return { name, extension : tryGetExtensionFromPath ( name ) } ;
780
793
}
@@ -785,7 +798,8 @@ function getFilenameWithExtensionOption(name: string, compilerOptions: CompilerO
785
798
}
786
799
787
800
if (
788
- ( endingPreference === ModuleSpecifierEnding . Minimal || endingPreference === ModuleSpecifierEnding . Index ) &&
801
+ ! isExportsWildcard &&
802
+ ( allowedEndings [ 0 ] === ModuleSpecifierEnding . Minimal || allowedEndings [ 0 ] === ModuleSpecifierEnding . Index ) &&
789
803
fileExtensionIsOneOf ( name , [ Extension . Js , Extension . Jsx , Extension . Ts , Extension . Tsx , Extension . Dts ] )
790
804
) {
791
805
return { name : removeFileExtension ( name ) , extension : tryGetExtensionFromPath ( name ) } ;
@@ -814,12 +828,13 @@ function addCompletionEntriesFromPaths(
814
828
const lengthB = typeof patternB === "object" ? patternB . prefix . length : b . length ;
815
829
return compareValues ( lengthB , lengthA ) ;
816
830
} ;
817
- return addCompletionEntriesFromPathsOrExports ( result , fragment , baseDirectory , extensionOptions , host , getOwnKeys ( paths ) , getPatternsForKey , comparePaths ) ;
831
+ return addCompletionEntriesFromPathsOrExports ( result , /*isExports*/ false , fragment , baseDirectory , extensionOptions , host , getOwnKeys ( paths ) , getPatternsForKey , comparePaths ) ;
818
832
}
819
833
820
834
/** @returns whether `fragment` was a match for any `paths` (which should indicate whether any other path completions should be offered) */
821
835
function addCompletionEntriesFromPathsOrExports (
822
836
result : NameAndKindSet ,
837
+ isExports : boolean ,
823
838
fragment : string ,
824
839
baseDirectory : string ,
825
840
extensionOptions : ExtensionOptions ,
@@ -857,7 +872,7 @@ function addCompletionEntriesFromPathsOrExports(
857
872
if ( typeof pathPattern === "string" || matchedPath === undefined || comparePaths ( key , matchedPath ) !== Comparison . GreaterThan ) {
858
873
pathResults . push ( {
859
874
matchedPattern : isMatch ,
860
- results : getCompletionsForPathMapping ( keyWithoutLeadingDotSlash , patterns , fragment , baseDirectory , extensionOptions , host )
875
+ results : getCompletionsForPathMapping ( keyWithoutLeadingDotSlash , patterns , fragment , baseDirectory , extensionOptions , isExports && isMatch , host )
861
876
. map ( ( { name, kind, extension } ) => nameAndKind ( name , kind , extension ) ) ,
862
877
} ) ;
863
878
}
@@ -956,6 +971,7 @@ function getCompletionEntriesForNonRelativeModules(
956
971
const conditions = getConditions ( compilerOptions , mode ) ;
957
972
addCompletionEntriesFromPathsOrExports (
958
973
result ,
974
+ /*isExports*/ true ,
959
975
fragmentSubpath ,
960
976
packageDirectory ,
961
977
extensionOptions ,
@@ -1001,6 +1017,7 @@ function getCompletionsForPathMapping(
1001
1017
fragment : string ,
1002
1018
packageDirectory : string ,
1003
1019
extensionOptions : ExtensionOptions ,
1020
+ isExportsWildcard : boolean ,
1004
1021
host : LanguageServiceHost ,
1005
1022
) : readonly NameAndKind [ ] {
1006
1023
if ( ! endsWith ( path , "*" ) ) {
@@ -1012,9 +1029,9 @@ function getCompletionsForPathMapping(
1012
1029
const remainingFragment = tryRemovePrefix ( fragment , pathPrefix ) ;
1013
1030
if ( remainingFragment === undefined ) {
1014
1031
const starIsFullPathComponent = path [ path . length - 2 ] === "/" ;
1015
- return starIsFullPathComponent ? justPathMappingName ( pathPrefix , ScriptElementKind . directory ) : flatMap ( patterns , pattern => getModulesForPathsPattern ( "" , packageDirectory , pattern , extensionOptions , host ) ?. map ( ( { name, ...rest } ) => ( { name : pathPrefix + name , ...rest } ) ) ) ;
1032
+ return starIsFullPathComponent ? justPathMappingName ( pathPrefix , ScriptElementKind . directory ) : flatMap ( patterns , pattern => getModulesForPathsPattern ( "" , packageDirectory , pattern , extensionOptions , isExportsWildcard , host ) ?. map ( ( { name, ...rest } ) => ( { name : pathPrefix + name , ...rest } ) ) ) ;
1016
1033
}
1017
- return flatMap ( patterns , pattern => getModulesForPathsPattern ( remainingFragment , packageDirectory , pattern , extensionOptions , host ) ) ;
1034
+ return flatMap ( patterns , pattern => getModulesForPathsPattern ( remainingFragment , packageDirectory , pattern , extensionOptions , isExportsWildcard , host ) ) ;
1018
1035
1019
1036
function justPathMappingName ( name : string , kind : ScriptElementKind . directory | ScriptElementKind . scriptElement ) : readonly NameAndKind [ ] {
1020
1037
return startsWith ( name , fragment ) ? [ { name : removeTrailingDirectorySeparator ( name ) , kind, extension : undefined } ] : emptyArray ;
@@ -1026,6 +1043,7 @@ function getModulesForPathsPattern(
1026
1043
packageDirectory : string ,
1027
1044
pattern : string ,
1028
1045
extensionOptions : ExtensionOptions ,
1046
+ isExportsWildcard : boolean ,
1029
1047
host : LanguageServiceHost ,
1030
1048
) : readonly NameAndKind [ ] | undefined {
1031
1049
if ( ! host . readDirectory ) {
@@ -1074,7 +1092,7 @@ function getModulesForPathsPattern(
1074
1092
if ( containsSlash ( trimmedWithPattern ) ) {
1075
1093
return directoryResult ( getPathComponents ( removeLeadingDirectorySeparator ( trimmedWithPattern ) ) [ 1 ] ) ;
1076
1094
}
1077
- const { name, extension } = getFilenameWithExtensionOption ( trimmedWithPattern , host . getCompilationSettings ( ) , extensionOptions ) ;
1095
+ const { name, extension } = getFilenameWithExtensionOption ( trimmedWithPattern , host . getCompilationSettings ( ) , extensionOptions , isExportsWildcard ) ;
1078
1096
return nameAndKind ( name , ScriptElementKind . scriptElement , extension ) ;
1079
1097
}
1080
1098
} ) ;
0 commit comments